Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * VDF support routines for PhysicsFS.
  3.  *
  4.  * This driver handles Gothic I/II VDF archives.
  5.  * This format (but not this driver) was designed by Piranha Bytes for
  6.  *  use wih the ZenGin engine.
  7.  *
  8.  * This file was written by Francesco Bertolaccini, based on the UNPK archiver
  9.  *  by Ryan C. Gordon and the works of degenerated1123 and Nico Bendlin.
  10.  */
  11.  
  12. #define __PHYSICSFS_INTERNAL__
  13. #include "physfs_internal.h"
  14.  
  15. #if PHYSFS_SUPPORTS_VDF
  16.  
  17. #include <time.h>
  18.  
  19. #define VDF_COMMENT_LENGTH 256
  20. #define VDF_SIGNATURE_LENGTH 16
  21. #define VDF_ENTRY_NAME_LENGTH 64
  22. #define VDF_ENTRY_DIR 0x80000000
  23.  
  24. static const char* VDF_SIGNATURE_G1 = "PSVDSC_V2.00\r\n\r\n";
  25. static const char* VDF_SIGNATURE_G2 = "PSVDSC_V2.00\n\r\n\r";
  26.  
  27.  
  28. static inline int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val)
  29. {
  30.     PHYSFS_uint32 v;
  31.     BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0);
  32.     *val = PHYSFS_swapULE32(v);
  33.     return 1;
  34. } /* readui32 */
  35.  
  36.  
  37. static PHYSFS_sint64 vdfDosTimeToEpoch(const PHYSFS_uint32 dostime)
  38. {
  39.     /* VDF stores timestamps as 32bit DOS dates: the seconds are counted in
  40.        2-seconds intervals and the years are counted since 1 Jan. 1980 */
  41.     struct tm t;
  42.     memset(&t, '\0', sizeof (t));
  43.     t.tm_year = ((int) ((dostime >> 25) & 0x7F)) + 80; /* 1980 to 1900 */
  44.     t.tm_mon = ((int) ((dostime >> 21) & 0xF)) - 1;  /* 1-12 to 0-11 */
  45.     t.tm_mday = (int) ((dostime >> 16) & 0x1F);
  46.     t.tm_hour = (int) ((dostime >> 11) & 0x1F);
  47.     t.tm_min = (int) ((dostime >> 5) & 0x3F);
  48.     t.tm_sec = ((int) ((dostime >> 0) & 0x1F)) * 2;  /* 2 seconds to 1. */
  49.     return (PHYSFS_sint64) mktime(&t);
  50. } /* vdfDosTimeToEpoch */
  51.  
  52.  
  53. static int vdfLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count,
  54.                           const PHYSFS_sint64 ts, void *arc)
  55. {
  56.     PHYSFS_uint32 i;
  57.  
  58.     for (i = 0; i < count; i++)
  59.     {
  60.         char name[VDF_ENTRY_NAME_LENGTH + 1];
  61.         int namei;
  62.         PHYSFS_uint32 jump, size, type, attr;
  63.  
  64.         BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, sizeof (name) - 1), 0);
  65.         BAIL_IF_ERRPASS(!readui32(io, &jump), 0);
  66.         BAIL_IF_ERRPASS(!readui32(io, &size), 0);
  67.         BAIL_IF_ERRPASS(!readui32(io, &type), 0);
  68.         BAIL_IF_ERRPASS(!readui32(io, &attr), 0);
  69.  
  70.         /* Trim whitespace off the end of the filename */
  71.         name[VDF_ENTRY_NAME_LENGTH] = '\0';  /* always null-terminated. */
  72.         for (namei = VDF_ENTRY_NAME_LENGTH - 1; namei >= 0; namei--)
  73.         {
  74.             /* We assume the filenames are low-ASCII; consider the archive
  75.                corrupt if we see something above 127, since we don't know the
  76.                encoding. (We can change this later if we find out these exist
  77.                and are intended to be, say, latin-1 or UTF-8 encoding). */
  78.             BAIL_IF(((PHYSFS_uint8) name[namei]) > 127, PHYSFS_ERR_CORRUPT, 0);
  79.  
  80.             if (name[namei] == ' ')
  81.                 name[namei] = '\0';
  82.             else
  83.                 break;
  84.         } /* for */
  85.  
  86.         BAIL_IF(!name[0], PHYSFS_ERR_CORRUPT, 0);
  87.         if (!(type & VDF_ENTRY_DIR)) {
  88.             BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, ts, ts, jump, size), 0);
  89.         }
  90.     } /* for */
  91.  
  92.     return 1;
  93. } /* vdfLoadEntries */
  94.  
  95.  
  96. static void *VDF_openArchive(PHYSFS_Io *io, const char *name,
  97.                              int forWriting, int *claimed)
  98. {
  99.     PHYSFS_uint8 ignore[16];
  100.     PHYSFS_uint8 sig[VDF_SIGNATURE_LENGTH];
  101.     PHYSFS_uint32 count, timestamp, version, dataSize, rootCatOffset;
  102.     void *unpkarc;
  103.  
  104.     assert(io != NULL); /* shouldn't ever happen. */
  105.  
  106.     BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
  107.  
  108.     /* skip the 256-byte comment field. */
  109.     BAIL_IF_ERRPASS(!io->seek(io, VDF_COMMENT_LENGTH), NULL);
  110.  
  111.     BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, sig, sizeof (sig)), NULL);
  112.  
  113.     if ((memcmp(sig, VDF_SIGNATURE_G1, VDF_SIGNATURE_LENGTH) != 0) &&
  114.         (memcmp(sig, VDF_SIGNATURE_G2, VDF_SIGNATURE_LENGTH) != 0))
  115.     {
  116.         BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
  117.     } /* if */
  118.  
  119.     *claimed = 1;
  120.  
  121.     BAIL_IF_ERRPASS(!readui32(io, &count), NULL);
  122.     BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), NULL);  /* numFiles */
  123.     BAIL_IF_ERRPASS(!readui32(io, &timestamp), NULL);
  124.     BAIL_IF_ERRPASS(!readui32(io, &dataSize), NULL);  /* dataSize */
  125.     BAIL_IF_ERRPASS(!readui32(io, &rootCatOffset), NULL);  /* rootCatOff */
  126.     BAIL_IF_ERRPASS(!readui32(io, &version), NULL);
  127.  
  128.     BAIL_IF(version != 0x50, PHYSFS_ERR_UNSUPPORTED, NULL);
  129.  
  130.     BAIL_IF_ERRPASS(!io->seek(io, rootCatOffset), NULL);
  131.  
  132.     unpkarc = UNPK_openArchive(io);
  133.     BAIL_IF_ERRPASS(!unpkarc, NULL);
  134.  
  135.     if (!vdfLoadEntries(io, count, vdfDosTimeToEpoch(timestamp), unpkarc))
  136.     {
  137.         UNPK_abandonArchive(unpkarc);
  138.         return NULL;
  139.     } /* if */
  140.  
  141.     return unpkarc;
  142. } /* VDF_openArchive */
  143.  
  144.  
  145. const PHYSFS_Archiver __PHYSFS_Archiver_VDF =
  146. {
  147.     CURRENT_PHYSFS_ARCHIVER_API_VERSION,
  148.     {
  149.         "VDF",
  150.         "Gothic I/II engine format",
  151.         "Francesco Bertolaccini <bertolaccinifrancesco@gmail.com>",
  152.         "https://github.com/frabert",
  153.         0,  /* supportsSymlinks */
  154.     },
  155.     VDF_openArchive,
  156.     UNPK_enumerate,
  157.     UNPK_openRead,
  158.     UNPK_openWrite,
  159.     UNPK_openAppend,
  160.     UNPK_remove,
  161.     UNPK_mkdir,
  162.     UNPK_stat,
  163.     UNPK_closeArchive
  164. };
  165.  
  166. #endif /* defined PHYSFS_SUPPORTS_VDF */
  167.  
  168. /* end of physfs_archiver_vdf.c ... */
  169.