#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;
);
}
+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)
{
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(×tamp);
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);
+ printf("Directories offset %08x\n", header->directories_offset);
printf("Directories size %08x\n", header->directories_bytes);
printf("Directories entries %04x\n", header->directories_entries);
printf("Files offset %08x\n", header->files_offset);
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;
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 Size %06x @ %08x payload size %06x @ %08x", i, q->entry_bytes, header->files_offset +(uint32_t)((void *)q - (void *)fileent), q->payload_bytes, q->payload_offset);
+ timestamp = msdos2unixtime(q->timestamp, q->datestamp);
+ datetime = ctime(×tamp);
+ 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);
printf(" \"%s\\%s\"\n", dirents[q->dir_index].entry->name, q->name);
+ char *dir_name = strdup(dirents[q->dir_index].entry->name);
+ for (char *p = dir_name; *p; ++p)
+ if (*p == '\\') *p = '/';
+ if (debug) printf("Parsing '%s' (%ld bytes)\n", dir_name, strlen(dir_name));
+
int dir_fd = AT_FDCWD;
int dir_fd_last = dir_fd;
if (extract) {
- char *dir = dirents[q->dir_index].entry->name;
-
- for (char *sep = dir;; ++sep) {
- if (*sep == '\\' || *sep == 0) {
- char temp = *sep;
- *sep = 0;
-
- if (strlen(dir) > 0) {
- int ret;
-
- if (debug)
- printf("Creating directory '%s': ", dir);
-
- if (!dirents[q->dir_index].created && mkdirat(dir_fd, dir, 0755) != 0) {
- if (errno != EEXIST) {
- *sep = temp;
- fprintf(stderr, "Error %d: cannot create directory %s\n", errno, dir);
+ if (!dirents[q->dir_index].created) {
+ char *component = dir_name;
+ bool last_component = false;
+ for (char *sep = dir_name;; ++sep) {
+ if (*sep == '/' || *sep == 0) {
+ if (*sep == 0)
+ last_component = true;
+ char temp = *sep;
+ *sep = 0;
+ if (strlen(component) > 0) {
+ if (debug) printf("Component: '%s' %ld bytes @ %p\n", component, strlen(component), component);
+ int ret = mkdirat(dir_fd, component, 0755);
+ if (ret == 0 || (ret == -1 && errno == EEXIST)) {
+ if (debug)
+ printf("Directory '%s' %s\n", component, ret == 0 ? "created" : "already exists");
+ dir_fd_last = dir_fd;
+ dir_fd = openat(dir_fd_last, component, O_DIRECTORY);
+ close(dir_fd_last);
+ if (dir_fd == -1) {
+ fprintf(stderr, "Error %d: cannot open directory '%s': ", errno, component);
+ perror(NULL);
+ *sep = temp;
+ break; // cannot continue if unable to open a path component
+ } else if (last_component == true) {
+ dirents[q->dir_index].created = true;
+ }
+ } else {
+ fprintf(stderr, "Error %d: cannot create directory '%s': ", errno, component);
perror(NULL);
- goto dir_create_abort;
+ *sep = temp;
+ break; // leave the for() loop immediately
}
- if (debug)
- printf("Directory already exists: %s\n", dir);
- }
-
- dir_fd_last = dir_fd;
- if (debug)
- printf("Opening directory '%s'\n", dir);
- if ((dir_fd = openat(dir_fd_last, dir, O_DIRECTORY)) == -1) {
- *sep = temp;
- fprintf(stderr, "Error %d: cannot open directory %s\n", errno, dir);
- perror(NULL);
- goto dir_create_abort;
- }
-
- if (debug)
- printf("%s\n", "");
-
- if (dir_fd_last != AT_FDCWD) close(dir_fd_last);
-dir_exists:
+ } // strlen()
*sep = temp;
- dir = sep + 1;
- }
- }
- if (*sep == 0) break;
- }
- close(dir_fd);
- dir_fd = AT_FDCWD;
- dirents[q->dir_index].created = true;
- goto dir_create_done;
-
-dir_create_abort:
- if (debug)
- printf("%s", "\n");
- if (dir_fd_last != AT_FDCWD) {
- close(dir_fd_last);
- dir_fd_last = AT_FDCWD;
- }
- if (dir_fd != AT_FDCWD) {
- close(dir_fd);
- dir_fd = AT_FDCWD;
- }
-
+ component = sep + 1;
+ if (last_component) break;
+ } // separator
+ } // for()
+ if (dir_fd < 0)
+ dir_fd = AT_FDCWD;
+ } // .created == false
}
-dir_create_done:
- if (NULL);
- char *dir_name = strdup(dirents[q->dir_index].entry->name);
- for (char *p = dir_name; *p; ++p)
- if (*p == '\\') *p = '/';
-
- if (dir_fd == AT_FDCWD && (dir_fd = openat(AT_FDCWD, dir_name, O_DIRECTORY)) != -1) {
+ if ((dir_fd = openat(AT_FDCWD, dir_name, O_DIRECTORY)) != -1) {
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);
+ printf(" Creating payload file '%s/%s'\n", dir_name, z_filename);
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;
if ((z_qty = read(z_fd, buffer, q->payload_bytes)) == q->payload_bytes) {
+ if (debug) {
+ unsigned int checksum = 0x0;
+ for (int i = 0; i < q->payload_bytes / sizeof(unsigned int); ++i) {
+ checksum ^= ((unsigned int *)buffer)[i];
+ }
+ printf("Checksum: %08x\n", checksum);
+ }
if ((pay_qty = write(pay_fd, buffer, q->payload_bytes)) != q->payload_bytes)
fprintf(stderr, "Error: whilst writing payload expected %d bytes but wrote %ld\n", q->payload_bytes, pay_qty);
} 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);
}
- } else
+ } else {
fprintf(stderr, "Error %d: failed to create %s\n", errno, z_filename);
+ }
close(dir_fd);
} else {
- fprintf(stderr, "Error %d: cannot open directory at %d %s\n", errno, dir_fd, dir_name);
+ fprintf(stderr, "Error %d: cannot open directory at (%d) '%s': ", errno, dir_fd, dir_name);
perror(NULL);
}
-
+ free(dir_name);
q = (void *)q + q->entry_bytes;
}
free(fileent);