diff --git a/include/SDL3/SDL_filesystem.h b/include/SDL3/SDL_filesystem.h index c98c1b1a0..5ae90d3d8 100644 --- a/include/SDL3/SDL_filesystem.h +++ b/include/SDL3/SDL_filesystem.h @@ -244,16 +244,19 @@ typedef enum SDL_PathType 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; +/* SDL file times are 64-bit integers representing nanoseconds since the Unix epoch (Jan 1, 1970) + * + * They can be converted between to POSIX time_t values with SDL_NS_TO_SECONDS() and SDL_SECONDS_TO_NS(), and between Windows FILETIME values with SDL_FileTimeToWindows() and SDL_FileTimeFromWindows() + */ +typedef Sint64 SDL_FileTime; 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_PathType type; /* the path type */ + Uint64 size; /* the file size in bytes */ + SDL_FileTime create_time; /* the time when the path was created */ + SDL_FileTime modify_time; /* the last time the path was modified */ + SDL_FileTime access_time; /* the last time the path was read */ } SDL_PathInfo; /** @@ -318,17 +321,29 @@ extern DECLSPEC int SDLCALL SDL_RenamePath(const char *oldpath, const char *newp */ 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. +/* Converts an SDL file time into a Windows FILETIME (100-nanosecond intervals since January 1, 1601). + * + * This function 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 + * \param dwLowDateTime a pointer filled in with the low portion of the Windows FILETIME value + * \param dwHighDateTime 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); +extern DECLSPEC void SDLCALL SDL_FileTimeToWindows(SDL_FileTime ftime, Uint32 *dwLowDateTime, Uint32 *dwHighDateTime); + +/* Converts a Windows FILETIME (100-nanosecond intervals since January 1, 1601) to an SDL file time + * + * This function takes the two 32-bit values of the FILETIME structure as parameters. + * + * \param dwLowDateTime the low portion of the Windows FILETIME value + * \param dwHighDateTime the high portion of the Windows FILETIME value + * \returns the converted file time + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC SDL_FileTime SDLCALL SDL_FileTimeFromWindows(Uint32 dwLowDateTime, Uint32 dwHighDateTime); /* Ends C function definitions when using C++ */ #ifdef __cplusplus diff --git a/include/SDL3/SDL_timer.h b/include/SDL3/SDL_timer.h index e824ad7f7..d317bd81f 100644 --- a/include/SDL3/SDL_timer.h +++ b/include/SDL3/SDL_timer.h @@ -45,10 +45,12 @@ extern "C" { #define SDL_NS_PER_SECOND 1000000000LL #define SDL_NS_PER_MS 1000000 #define SDL_NS_PER_US 1000 -#define SDL_MS_TO_NS(MS) (((Uint64)(MS)) * SDL_NS_PER_MS) -#define SDL_NS_TO_MS(NS) ((NS) / SDL_NS_PER_MS) -#define SDL_US_TO_NS(US) (((Uint64)(US)) * SDL_NS_PER_US) -#define SDL_NS_TO_US(NS) ((NS) / SDL_NS_PER_US) +#define SDL_SECONDS_TO_NS(S) (((Uint64)(S)) * SDL_NS_PER_SECOND) +#define SDL_NS_TO_SECONDS(NS) ((NS) / SDL_NS_PER_SECOND) +#define SDL_MS_TO_NS(MS) (((Uint64)(MS)) * SDL_NS_PER_MS) +#define SDL_NS_TO_MS(NS) ((NS) / SDL_NS_PER_MS) +#define SDL_US_TO_NS(US) (((Uint64)(US)) * SDL_NS_PER_US) +#define SDL_NS_TO_US(NS) ((NS) / SDL_NS_PER_US) /** * Get the number of milliseconds since SDL library initialization. diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index c95e658c3..eb41c61db 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -999,6 +999,7 @@ SDL3_0.0.0 { SDL_RemoveStoragePath; SDL_RenameStoragePath; SDL_GetStoragePathInfo; + SDL_FileTimeFromWindows; # 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 f31ea6a32..7d69981bf 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1024,3 +1024,4 @@ #define SDL_RemoveStoragePath SDL_RemoveStoragePath_REAL #define SDL_RenameStoragePath SDL_RenameStoragePath_REAL #define SDL_GetStoragePathInfo SDL_GetStoragePathInfo_REAL +#define SDL_FileTimeFromWindows SDL_FileTimeFromWindows_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 9202049c4..4266fea33 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1049,3 +1049,4 @@ SDL_DYNAPI_PROC(int,SDL_EnumerateStorageDirectory,(SDL_Storage *a, const char *b SDL_DYNAPI_PROC(int,SDL_RemoveStoragePath,(SDL_Storage *a, const char *b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_RenameStoragePath,(SDL_Storage *a, const char *b, const char *c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_GetStoragePathInfo,(SDL_Storage *a, const char *b, SDL_PathInfo *c),(a,b,c),return) +SDL_DYNAPI_PROC(SDL_FileTime,SDL_FileTimeFromWindows,(Uint32 a, Uint32 b),(a,b),return) diff --git a/src/filesystem/SDL_filesystem.c b/src/filesystem/SDL_filesystem.c index 014d65884..aa5c4ddd3 100644 --- a/src/filesystem/SDL_filesystem.c +++ b/src/filesystem/SDL_filesystem.c @@ -22,27 +22,38 @@ #include "SDL_internal.h" #include "SDL_sysfilesystem.h" -void SDL_FileTimeToWindows(Sint64 ftime, Uint32 *low, Uint32 *high) +static const Sint64 delta_1601_epoch_100ns = 11644473600ll * 10000000ll; // [100 ns] (100 ns units between 1/1/1601 and 1/1/1970, 11644473600 seconds) + +void SDL_FileTimeToWindows(SDL_FileTime ftime, Uint32 *dwLowDateTime, Uint32 *dwHighDateTime) { - const Sint64 delta_1601_epoch_s = 11644473600ull; // [seconds] (seconds between 1/1/1601 and 1/1/1970, 11644473600 seconds) + Uint64 wtime; - Sint64 cvt = (ftime + delta_1601_epoch_s) * (SDL_NS_PER_SECOND / 100ull); // [100ns] (adjust to epoch and convert nanoseconds to 1/100th nanosecond units). + // Convert ftime to 100ns units + Sint64 ftime_100ns = (ftime / 100); - // 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 (ftime_100ns < 0 && -ftime_100ns > delta_1601_epoch_100ns) { + // If we're trying to show a timestamp from before before the Windows epoch, (Jan 1, 1601), clamp it to zero + wtime = 0; + } else { + wtime = (Uint64)(delta_1601_epoch_100ns + ftime_100ns); } - if (low) { - *low = (Uint32) cvt; + if (dwLowDateTime) { + *dwLowDateTime = (Uint32)wtime; } - if (high) { - *high = (Uint32) (cvt >> 32); + if (dwHighDateTime) { + *dwHighDateTime = (Uint32)(wtime >> 32); } } +SDL_FileTime SDL_FileTimeFromWindows(Uint32 dwLowDateTime, Uint32 dwHighDateTime) +{ + Uint64 wtime = (((Uint64)dwHighDateTime << 32) | dwLowDateTime); + + return (Sint64)(wtime - delta_1601_epoch_100ns) * 100; +} + int SDL_RemovePath(const char *path) { if (!path) { diff --git a/src/filesystem/posix/SDL_sysfsops.c b/src/filesystem/posix/SDL_sysfsops.c index f793888c3..20ffce905 100644 --- a/src/filesystem/posix/SDL_sysfsops.c +++ b/src/filesystem/posix/SDL_sysfsops.c @@ -124,12 +124,9 @@ int SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info) 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; + info->create_time = (SDL_FileTime)SDL_SECONDS_TO_NS(statbuf.st_ctime); + info->modify_time = (SDL_FileTime)SDL_SECONDS_TO_NS(statbuf.st_mtime); + info->access_time = (SDL_FileTime)SDL_SECONDS_TO_NS(statbuf.st_atime); return 0; } diff --git a/src/filesystem/windows/SDL_sysfsops.c b/src/filesystem/windows/SDL_sysfsops.c index cea3ac295..177d4ba80 100644 --- a/src/filesystem/windows/SDL_sysfsops.c +++ b/src/filesystem/windows/SDL_sysfsops.c @@ -140,18 +140,6 @@ int SDL_SYS_CreateDirectory(const char *path) 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_GetPathInfo(const char *path, SDL_PathInfo *info) { WCHAR *wpath = WIN_UTF8ToString(path); @@ -177,9 +165,9 @@ int SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info) 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); + info->create_time = SDL_FileTimeFromWindows(winstat.ftCreationTime.dwLowDateTime, winstat.ftCreationTime.dwHighDateTime); + info->modify_time = SDL_FileTimeFromWindows(winstat.ftLastWriteTime.dwLowDateTime, winstat.ftLastWriteTime.dwHighDateTime); + info->access_time = SDL_FileTimeFromWindows(winstat.ftLastAccessTime.dwLowDateTime, winstat.ftLastAccessTime.dwHighDateTime); return 1; }