Prechádzať zdrojové kódy

zip: Calculate DOS time to PhysicsFS time by hand.

This is _much_ faster than using mktime() on some platforms, but this
currently doesn't take timezone into account (so these are now UTC-based
times). The standard ZIP file datetime runs on 2-second increments and has no
timezone info, so being more "right" doesn't actually mean that much here, but
I'll add something to figure out the UTC->local offset to the platform layer
sometime later.

There are also extensions that add better time info in the extended field, and
we should try to support those later, too.
Ryan C. Gordon 1 týždeň pred
rodič
commit
a802bc6119
1 zmenil súbory, kde vykonal 17 pridanie a 21 odobranie
  1. 17 21
      src/physfs_archiver_zip.c

+ 17 - 21
src/physfs_archiver_zip.c

@@ -13,7 +13,6 @@
 #if PHYSFS_SUPPORTS_ZIP
 
 #include <errno.h>
-#include <time.h>
 
 #if (PHYSFS_BYTEORDER == PHYSFS_LIL_ENDIAN)
 #define MINIZ_LITTLE_ENDIAN 1
@@ -969,27 +968,24 @@ static inline int zip_has_symlink_attr(const ZIPentry *entry,
 
 static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime)
 {
-    PHYSFS_uint32 dosdate;
-    struct tm unixtime;
-    memset(&unixtime, '\0', sizeof (unixtime));
-
-    dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF);
+    const PHYSFS_uint32 dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF);
     dostime &= 0xFFFF;
 
-    /* dissect date */
-    unixtime.tm_year = ((dosdate >> 9) & 0x7F) + 80;
-    unixtime.tm_mon  = ((dosdate >> 5) & 0x0F) - 1;
-    unixtime.tm_mday = ((dosdate     ) & 0x1F);
-
-    /* dissect time */
-    unixtime.tm_hour = ((dostime >> 11) & 0x1F);
-    unixtime.tm_min  = ((dostime >>  5) & 0x3F);
-    unixtime.tm_sec  = ((dostime <<  1) & 0x3E);
-
-    /* let mktime calculate daylight savings time. */
-    unixtime.tm_isdst = -1;
-
-    return ((PHYSFS_sint64) mktime(&unixtime));
+    const int m = (int) ((dosdate >> 5) & 0x0F);
+    const int d = (int) ((dosdate >> 0) & 0x1F) + 1;
+    const int y = (int) (((dosdate >> 9) & 0x7F) + 1980) - (m <= 2 ? 1 : 0);
+    const int hour   = (int) ((dostime >> 11) & 0x1F);
+    const int minute = (int) ((dostime >>  5) & 0x3F);
+    const int sec    = (int) ((dostime <<  1) & 0x3E);
+
+    // days since 1/1/1970: https://howardhinnant.github.io/date_algorithms.html#days_from_civil
+    const int era = ((y >= 0) ? y : (y - 399)) / 400;
+    const unsigned int yoe = (unsigned int) (y - era * 400);      // [0, 399]
+    const unsigned int doy = (((153 * ((m > 2) ? (m - 3) : (m + 9))) + 2) / 5) + (d - 1);  // [0, 365]
+    const unsigned int doe = (yoe * 365) + (yoe / 4) - (yoe / 100) + doy;         // [0, 146096]
+    const int days = (era * 146097) + ((int) doe) - 719468;
+    const int seconds = (hour * (60 * 60)) + (minute * 60) + (sec);
+    return (((PHYSFS_sint64) days) * 86400) + seconds;
 } /* zip_dos_time_to_physfs_time */
 
 
@@ -1021,7 +1017,7 @@ static ZIPentry *zip_load_entry(ZIPinfo *info, const int zip64,
     BAIL_IF_ERRPASS(!readui16(io, &entry.general_bits), NULL);  /* general bits */
     BAIL_IF_ERRPASS(!readui16(io, &entry.compression_method), NULL);
     BAIL_IF_ERRPASS(!readui32(io, &entry.dos_mod_time), NULL);
-    entry.last_mod_time = zip_dos_time_to_physfs_time(entry.dos_mod_time);
+    entry.last_mod_time = zip_dos_time_to_physfs_time(entry.dos_mod_time);  /* !!! FIXME: there are extended fields that can get you off gross old DOS time format. */
     BAIL_IF_ERRPASS(!readui32(io, &entry.crc), NULL);
     BAIL_IF_ERRPASS(!readui32(io, &ui32), NULL);
     entry.compressed_size = (PHYSFS_uint64) ui32;