From 747300b3562b259645731eaa6a78782c1ae7e6fd Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 17 Mar 2024 13:11:13 -0700
Subject: [PATCH] SDL file times are 64-bit integers representing nanoseconds
since the Unix epoch
---
include/SDL3/SDL_filesystem.h | 41 ++++++++++++++++++---------
include/SDL3/SDL_timer.h | 10 ++++---
src/dynapi/SDL_dynapi.sym | 1 +
src/dynapi/SDL_dynapi_overrides.h | 1 +
src/dynapi/SDL_dynapi_procs.h | 1 +
src/filesystem/SDL_filesystem.c | 33 ++++++++++++++-------
src/filesystem/posix/SDL_sysfsops.c | 9 ++----
src/filesystem/windows/SDL_sysfsops.c | 18 ++----------
8 files changed, 65 insertions(+), 49 deletions(-)
diff --git a/include/SDL3/SDL_filesystem.h b/include/SDL3/SDL_filesystem.h
index c98c1b1a04b43..5ae90d3d8009e 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(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 void SDLCALL SDL_FileTimeToWindows(Sint64 ftime, Uint32 *low, Uint32 *high);
+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 e824ad7f7e099..d317bd81f0abc 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 c95e658c34170..eb41c61db6063 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 f31ea6a323928..7d69981bfd6a7 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 9202049c43970..4266fea3339d5 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 014d658846150..aa5c4ddd354e4 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 f793888c3b458..20ffce90568c0 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 cea3ac295bf85..177d4ba805e3d 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;
}