Add Archive timestamp decoding
[installshield_z.git] / installshield_main.c
index 9e95071..4369421 100644 (file)
 #include <unistd.h>
 #include <errno.h>
 #include <string.h>
+#include <time.h>
+#include <utime.h>
 
+#include "implode.h"
 #include "installshield_z.h"
 
 static const size_t BUFFER_SIZE = 0x4000;
@@ -31,6 +34,20 @@ usage(const char *name)
        );
 }
 
+time_t msdos2unixtime(uint16_t time, uint16_t date)
+{
+       if (debug) printf("Date: %04x Time %04x\n", date, time);
+       struct tm dt;
+       dt.tm_year              =((date & 0xFE00) >>  9) + 80;
+       dt.tm_mon               = (date & 0x01E0) >>  5;
+       dt.tm_mday              = (date & 0x001F) >>  0;
+       dt.tm_hour              = (time & 0xF800) >> 11;
+       dt.tm_min               = (time & 0x07E0) >>  5;
+       dt.tm_sec               = (time & 0x001F) >>  0;
+
+       return mktime(&dt);
+}
+
 int
 headers_decode(int z_fd)
 {
@@ -43,8 +60,10 @@ headers_decode(int z_fd)
                offset = lseek(z_fd, offset, SEEK_SET);
                if ((qty = read(z_fd, header, Z_FILE_HEADER_BYTES))< Z_FILE_HEADER_BYTES)
                        fprintf(stderr, "Warning: Tried to read %ld header bytes but only got %ld. Will do my best\n", Z_FILE_HEADER_BYTES, qty);
+               time_t timestamp = msdos2unixtime(header->timestamp, header->datestamp);
+               char *datetime = ctime(&timestamp);
                printf("Magic               %08x\n", header->magic);
-               printf("CRC32               %08x\n", header->__crc32);
+               printf("Timestamp           %s"  , datetime);
                printf("File size           %08x\n", header->file_bytes);
                printf("Payload size        %08x\n", header->payload_bytes);
                printf("Directories offset  %08x\n", header->directories_offset);
@@ -64,7 +83,7 @@ headers_decode(int z_fd)
 
                        printf("Directory entries: %d ( %08x bytes @ 0x%08x)\n", header->directories_entries, header->directories_bytes, header->directories_offset);
 
-                       struct ll_dirents dirents[header->directories_entries];
+                       struct dirent dirents[header->directories_entries];
                        for (int i = 0; i < header->directories_entries; ++i) {
                                printf(" Entry %4d Size %x @ %08x    \"%s\"\n", i, p->entry_bytes, header->directories_offset + (uint32_t)((void *)p - (void *)dirent), p->name);
                                dirents[i].entry = p;
@@ -80,7 +99,9 @@ headers_decode(int z_fd)
                                printf("File entries: %d ( %08x bytes @ 0x%08x)\n", header->files_entries, header->files_bytes, header->files_offset);
 
                                for (int i = 0; i < header->files_entries; ++i) {
-                                       printf(" Entry %4d uses %06x bytes @ %08x. file-size %08x (payload %08x @ %08x)", i, q->entry_bytes, header->files_offset +(uint32_t)((void *)q - (void *)fileent), q->file_bytes, q->payload_bytes, q->payload_offset);
+                                       timestamp = msdos2unixtime(q->timestamp, q->datestamp);
+                                       datetime = ctime(&timestamp);
+                                       printf(" Entry %4d uses %06x bytes @ %08x. Date %s File-size %08x (payload %08x @ %08x)", i, q->entry_bytes, header->files_offset +(uint32_t)((void *)q - (void *)fileent), datetime, q->file_bytes, q->payload_bytes, q->payload_offset);
 
                                        if (debug) {
                                                printf(" next @ %08x", q->payload_offset + q->payload_bytes);
@@ -145,7 +166,12 @@ headers_decode(int z_fd)
                                                unsigned int z_filename_len = strlen(q->name) + 4;
                                                char z_filename[z_filename_len];
                                                strncpy(z_filename, q->name, z_filename_len);
-                                               strncat(z_filename, ".z", 2);
+                                               strncat(z_filename, ".z", 3);
+
+                                               char filepath[strlen(dir_name) + z_filename_len];
+                                               strncpy(filepath, dir_name, strlen(dir_name) + 1);
+                                               strncat(filepath, "/", 2);
+                                               strncat(filepath, z_filename, z_filename_len);
 
                                                if (debug)
                                                        printf(" Creating payload file '%s/%s'\n", dir_name,  z_filename);
@@ -153,6 +179,9 @@ headers_decode(int z_fd)
                                                if ((offset = lseek(z_fd, q->payload_offset, SEEK_SET)) == q->payload_offset) {
                                                        void *buffer;
                                                        if ((buffer = calloc(q->payload_bytes, 1)) != 0) {
+                                                               struct utimbuf filetime;
+                                                               filetime.actime  = timestamp;
+                                                               filetime.modtime = timestamp;
                                                                int pay_fd;
                                                                if ((pay_fd = openat(dir_fd, z_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644))) {
                                                                        ssize_t z_qty, pay_qty;
@@ -169,6 +198,48 @@ headers_decode(int z_fd)
                                                                        } else
                                                                                fprintf(stderr, "Error: whilst reading payload expected %d bytes but read %ld\n", q->payload_bytes, z_qty);
                                                                        close(pay_fd);
+                                                                       if(utime(filepath, &filetime) == -1) {
+                                                                               fprintf(stderr, "Error: unable to set timestamp on '%s' (%d) ", filepath, errno);
+                                                                               perror(NULL);
+                                                                       }
+                                                               }
+                                                               if (q->payload_bytes >= 4) {
+                                                                       uint8_t comp_type = *(uint8_t *)buffer;
+                                                                       uint8_t dict_size = *(uint8_t *)(buffer + 1);
+                                                                       if (comp_type >= IMPLODE_BINARY && comp_type <= IMPLODE_ASCII && dict_size >= IMPLODE_DICT_1K && dict_size <= IMPLODE_DICT_4K) {
+                                                                               void * buffer_decomp;
+                                                                               if ((buffer_decomp = calloc(q->file_bytes, 1)) != 0) {
+                                                                                       uint32_t decomp_bytes = q->file_bytes;
+                                                                                       if (explode(buffer, q->payload_bytes, buffer_decomp, &decomp_bytes)) {
+                                                                                               if (decomp_bytes > q->file_bytes) {
+                                                                                                       fprintf(stderr, "Expected %d uncompressed bytes but got %d\n", q->file_bytes, decomp_bytes);
+                                                                                               }
+                                                                                               int decomp_fd;
+                                                                                               if ((decomp_fd = openat(dir_fd, q->name, O_WRONLY | O_CREAT | O_TRUNC, 0644))) {
+                                                                                                       ssize_t decomp_qty;
+                                                                                                       if (debug)
+                                                                                                               printf("Creating uncompressed file '%s/%s'\n",dir_name,  q->name);
+                                                                                                       if ((decomp_qty = write(decomp_fd, buffer_decomp, q->file_bytes)) != q->file_bytes)
+                                                                                                               fprintf(stderr, "Error: whilst writing decompressed data expected %d bytes but wrote %ld\n", q->file_bytes, decomp_qty);
+                                                                                                       close(decomp_fd);
+                                                                                                       filepath[strlen(filepath) - 2] = 0;
+                                                                                                       if(utime(filepath, &filetime) == -1) {
+                                                                                                               fprintf(stderr, "Error: unable to set timestamp on '%s' (%d) ", filepath, errno);
+                                                                                                               perror(NULL);
+                                                                                                       }
+                                                                                               }
+                                                                                       } else {
+                                                                                               fprintf(stderr, "Error: failed to decompress '%s', payload: %d decompressed: %d\n", q->name, q->payload_bytes, decomp_bytes);
+                                                                                       }
+                                                                                       free(buffer_decomp);
+                                                                               } else {
+                                                                                       fprintf(stderr, "Error: unable to allocate %d bytes for decompression buffer\n", q->file_bytes);
+                                                                               }
+                                                                       } else {
+                                                                               fprintf(stderr, "Does not look like TTComp compressed data. header: %04x\n", *(uint16_t *)buffer);
+                                                                       }
+                                                               } else {
+                                                                       fprintf(stderr, "Error: payload %d bytes smaller than minimum allowed TTComp of 4\n", q->payload_bytes);
                                                                }
                                                                free(buffer);
                                                        }