--- /dev/null
+/*
+ InstallShield Z file format
+ Copyright 2015 <hacker@iam.tj>
+ Licenced on the terms of then GNU General Public Licence version 3
+
+ Read and extract InstallShield .Z archive files
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include "installshield_z.h"
+
+static const size_t BUFFER_SIZE = 0x4000;
+static bool debug = 0;
+static bool extract = 0;
+
+void
+usage(const char *name)
+{
+ printf("usage: %s %s\n\n", name,
+ " FILENAME\n"
+ " [-x|--extract]"
+ );
+}
+
+int
+headers_decode(int z_fd)
+{
+ int result = -1;
+ struct archive_header *header = NULL;
+ off_t offset = 0;
+ ssize_t qty = 0;
+
+ if (z_fd != -1 && (header = calloc(BUFFER_SIZE, 1))) {
+ 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);
+ printf("Magic %08x\n", header->magic);
+ printf("CRC32 %08x\n", header->__crc32);
+ 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 size %08x\n", header->directories_bytes);
+ printf("Directories entries %04x\n", header->directories_entries);
+ printf("Files offset %08x\n", header->files_offset);
+ printf("Files size %08x\n", header->files_bytes);
+ printf("Files entries %04x\n", header->files_entries);
+
+ offset = lseek(z_fd, header->directories_offset, SEEK_SET);
+ struct dirent_prefix *dirent, *p;
+
+ if (offset == header->directories_offset && ((dirent = p = calloc(header->directories_bytes, 1)))) {
+
+ if ((qty = read(z_fd, dirent, header->directories_bytes)) != header->directories_bytes)
+ fprintf(stderr, "Warning: Tried to read %d directory bytes but only got %ld. Will do my best\n", header->directories_bytes, qty);
+
+ 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];
+ 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;
+ p = (void *)p + p->entry_bytes;
+ }
+
+ offset = lseek(z_fd, header->files_offset, SEEK_SET);
+ struct fileent_prefix *fileent, *q;
+ if (offset == header->files_offset && ((fileent = q = calloc(header->files_bytes, 1)))) {
+ if ((qty = read(z_fd, fileent, header->files_bytes)) != header->files_bytes)
+ fprintf(stderr, "Warning: Tried to read %d file entry bytes but only got %ld. Will do my best\n", header->files_bytes, qty);
+
+ 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);
+
+ if (debug) {
+ printf(" next @ %08x", q->payload_offset + q->payload_bytes);
+ for (int r = 0; r < 0x1E; ++r)
+ printf(" %02x", *(unsigned char *)(r + (void *)q));
+ }
+
+ printf(" \"%s\\%s\"\n", dirents[q->dir_index].entry->name, q->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);
+ perror(NULL);
+ goto dir_create_abort;
+ }
+ 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:
+ *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;
+ }
+
+ }
+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) {
+
+ 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);
+
+ if (debug)
+ 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) {
+ 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 ((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);
+ }
+ free(buffer);
+ }
+ } 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);
+ perror(NULL);
+ }
+
+ q = (void *)q + q->entry_bytes;
+ }
+ free(fileent);
+ }
+ free(dirent);
+ } else
+ fprintf(stderr, "Error: %s\n", "failed to seek or couldn't allocate memory for directories");
+
+ free(header);
+ result = 0;
+ }
+ return result;
+}
+
+int
+main(int argc, char **argv, char **env)
+{
+ int result = -1;
+ int fd = 0;
+
+ if (argc < 2 ) {
+ usage(argv[0]);
+ return 0;
+ }
+
+ for (char **e = env; *e; ++e) {
+ if (strncmp("DEBUG", *e, 5) == 0)
+ debug = true;
+ }
+ for (char **a = argv; *a; ++a) {
+ if (debug) printf("Arg: %s\n", *a);
+ if (strncmp("-x", *a, 2) == 0 || strncmp("--extract", *a, 9) == 0)
+ extract = true;
+ }
+
+ if ((fd = open(argv[1], O_RDONLY)) != -1) {
+ result = headers_decode(fd);
+ close(fd);
+ } else {
+ fprintf(stderr, "Error: could not open %s\n", argv[0]);
+ return errno;
+ }
+
+
+ return result;
+}
+
--- /dev/null
+/*
+ InstallShield Z file format
+ Copyright 2015 <hacker@iam.tj>
+ Licenced on the terms of then GNU General Public Licence version 3
+
+ Data structures used in InstallShield .Z archive files
+ */
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+const unsigned int file_magic = 0x8C655D12;
+const ssize_t Z_FILE_HEADER_BYTES = 0x100;
+
+/* __prefixed field purpose is not confirmed */
+struct __attribute__((packed)) archive_header {
+ uint32_t magic; // 0x00
+ uint16_t __version; // 0x04
+ uint16_t __flags; // 0x06
+ uint32_t __unknown08; // 0x08
+ uint16_t files_entries; // 0x0C
+ uint32_t __crc32; // 0x0E
+ uint32_t file_bytes; // 0x12
+ uint32_t payload_bytes; // 0x16
+ uint8_t __known1A; // 0x1A
+ uint16_t __unknown1B; // 0x1B
+ uint32_t __unknown1D; // 0x1D
+ uint32_t __unknown21; // 0x21
+ uint32_t __unknown25; // 0x25
+ uint32_t directories_offset; // 0x29
+ uint32_t directories_bytes; // 0x2D
+ uint16_t directories_entries; // 0x31
+ uint32_t files_offset; // 0x33
+ uint32_t files_bytes; // 0x37
+ unsigned char unknown3B[0x100 - 0x3B]; // 0x3B to 0xFF
+};
+
+struct __attribute__((packed)) dirent_prefix {
+ uint16_t __flags; // 0x00
+ uint16_t entry_bytes; // 0x02
+ uint16_t name_bytes; // 0x04
+ unsigned char name[]; // 0x06
+};
+
+struct __attribute__((packed)) dirent_suffix {
+ unsigned char __eos; // end of string NUL
+ uint32_t __unknown00; // 0x01 after end of directory_entry_prefix
+};
+
+struct __attribute__((packed)) fileent_prefix {
+ uint8_t __unknown00;
+ uint16_t dir_index;
+ uint32_t __unknown03;
+ uint32_t payload_bytes;
+ uint32_t payload_offset;
+ uint32_t __crc32;
+ uint32_t __unknown13;
+ uint16_t entry_bytes;
+ uint32_t __unknown01;
+ uint8_t name_bytes;
+ unsigned char name[];
+};
+
+struct __attribute__((packed)) fileent_suffix {
+ uint32_t __unkown00;
+ uint32_t __unkown04;
+ uint32_t __unkown08;
+};
+
+struct ll_dirents {
+ struct dirent_prefix *entry;
+ struct ll_dirents *next;
+ bool created;
+};
+
+struct ll_fileents {
+ struct fileent_prefix *entry;
+ struct ll_fileents *next;
+};