2 InstallShield Z file format
3 Copyright 2015 <hacker@iam.tj>
4 Licenced on the terms of then GNU General Public Licence version 3
6 Read and extract InstallShield .Z archive files
10 #include <sys/types.h>
22 #include "installshield_z.h"
24 static const size_t BUFFER_SIZE = 0x4000;
25 static bool debug = 0;
26 static bool extract = 0;
29 usage(const char *name)
31 printf("usage: %s %s\n\n", name,
37 time_t msdos2unixtime(uint16_t time, uint16_t date)
39 if (debug) printf("Date: %04x Time %04x\n", date, time);
41 dt.tm_year =((date & 0xFE00) >> 9) + 80;
42 dt.tm_mon = (date & 0x01E0) >> 5;
43 dt.tm_mday = (date & 0x001F) >> 0;
44 dt.tm_hour = (time & 0xF800) >> 11;
45 dt.tm_min = (time & 0x07E0) >> 5;
46 dt.tm_sec = (time & 0x001F) >> 0;
52 headers_decode(int z_fd)
55 struct archive_header *header = NULL;
59 if (z_fd != -1 && (header = calloc(BUFFER_SIZE, 1))) {
60 offset = lseek(z_fd, offset, SEEK_SET);
61 if ((qty = read(z_fd, header, Z_FILE_HEADER_BYTES))< Z_FILE_HEADER_BYTES)
62 fprintf(stderr, "Warning: Tried to read %ld header bytes but only got %ld. Will do my best\n", Z_FILE_HEADER_BYTES, qty);
63 time_t timestamp = msdos2unixtime(header->timestamp, header->datestamp);
64 char *datetime = ctime(×tamp);
65 printf("Magic %08x\n", header->magic);
66 printf("Timestamp %s" , datetime);
67 printf("File size %08x\n", header->file_bytes);
68 printf("Payload size %08x\n", header->payload_bytes);
69 printf("Directories offset %08x\n", header->directories_offset);
70 printf("Directories size %08x\n", header->directories_bytes);
71 printf("Directories entries %04x\n", header->directories_entries);
72 printf("Files offset %08x\n", header->files_offset);
73 printf("Files size %08x\n", header->files_bytes);
74 printf("Files entries %04x\n", header->files_entries);
76 offset = lseek(z_fd, header->directories_offset, SEEK_SET);
77 struct dirent_prefix *dirent, *p;
79 if (offset == header->directories_offset && ((dirent = p = calloc(header->directories_bytes, 1)))) {
81 if ((qty = read(z_fd, dirent, header->directories_bytes)) != header->directories_bytes)
82 fprintf(stderr, "Warning: Tried to read %d directory bytes but only got %ld. Will do my best\n", header->directories_bytes, qty);
84 printf("Directory entries: %d ( %08x bytes @ 0x%08x)\n", header->directories_entries, header->directories_bytes, header->directories_offset);
86 struct dirent dirents[header->directories_entries];
87 for (int i = 0; i < header->directories_entries; ++i) {
88 printf(" Entry %4d Size %x @ %08x \"%s\"\n", i, p->entry_bytes, header->directories_offset + (uint32_t)((void *)p - (void *)dirent), p->name);
90 p = (void *)p + p->entry_bytes;
93 offset = lseek(z_fd, header->files_offset, SEEK_SET);
94 struct fileent_prefix *fileent, *q;
95 if (offset == header->files_offset && ((fileent = q = calloc(header->files_bytes, 1)))) {
96 if ((qty = read(z_fd, fileent, header->files_bytes)) != header->files_bytes)
97 fprintf(stderr, "Warning: Tried to read %d file entry bytes but only got %ld. Will do my best\n", header->files_bytes, qty);
99 printf("File entries: %d ( %08x bytes @ 0x%08x)\n", header->files_entries, header->files_bytes, header->files_offset);
101 for (int i = 0; i < header->files_entries; ++i) {
102 timestamp = msdos2unixtime(q->timestamp, q->datestamp);
103 datetime = ctime(×tamp);
104 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);
107 printf(" next @ %08x", q->payload_offset + q->payload_bytes);
108 for (int r = 0; r < 0x1E; ++r)
109 printf(" %02x", *(unsigned char *)(r + (void *)q));
112 printf(" \"%s\\%s\"\n", dirents[q->dir_index].entry->name, q->name);
114 char *dir_name = strdup(dirents[q->dir_index].entry->name);
115 for (char *p = dir_name; *p; ++p)
116 if (*p == '\\') *p = '/';
117 if (debug) printf("Parsing '%s' (%ld bytes)\n", dir_name, strlen(dir_name));
119 int dir_fd = AT_FDCWD;
120 int dir_fd_last = dir_fd;
122 if (!dirents[q->dir_index].created) {
123 char *component = dir_name;
124 bool last_component = false;
125 for (char *sep = dir_name;; ++sep) {
126 if (*sep == '/' || *sep == 0) {
128 last_component = true;
131 if (strlen(component) > 0) {
132 if (debug) printf("Component: '%s' %ld bytes @ %p\n", component, strlen(component), component);
133 int ret = mkdirat(dir_fd, component, 0755);
134 if (ret == 0 || (ret == -1 && errno == EEXIST)) {
136 printf("Directory '%s' %s\n", component, ret == 0 ? "created" : "already exists");
137 dir_fd_last = dir_fd;
138 dir_fd = openat(dir_fd_last, component, O_DIRECTORY);
141 fprintf(stderr, "Error %d: cannot open directory '%s': ", errno, component);
144 break; // cannot continue if unable to open a path component
145 } else if (last_component == true) {
146 dirents[q->dir_index].created = true;
149 fprintf(stderr, "Error %d: cannot create directory '%s': ", errno, component);
152 break; // leave the for() loop immediately
157 if (last_component) break;
162 } // .created == false
164 if ((dir_fd = openat(AT_FDCWD, dir_name, O_DIRECTORY)) != -1) {
166 unsigned int z_filename_len = strlen(q->name) + 4;
167 char z_filename[z_filename_len];
168 strncpy(z_filename, q->name, z_filename_len);
169 strncat(z_filename, ".z", 3);
171 char filepath[strlen(dir_name) + z_filename_len];
172 strncpy(filepath, dir_name, strlen(dir_name) + 1);
173 strncat(filepath, "/", 2);
174 strncat(filepath, z_filename, z_filename_len);
177 printf(" Creating payload file '%s/%s'\n", dir_name, z_filename);
179 if ((offset = lseek(z_fd, q->payload_offset, SEEK_SET)) == q->payload_offset) {
181 if ((buffer = calloc(q->payload_bytes, 1)) != 0) {
182 struct utimbuf filetime;
183 filetime.actime = timestamp;
184 filetime.modtime = timestamp;
186 if ((pay_fd = openat(dir_fd, z_filename, O_WRONLY | O_CREAT | O_TRUNC, 0644))) {
187 ssize_t z_qty, pay_qty;
188 if ((z_qty = read(z_fd, buffer, q->payload_bytes)) == q->payload_bytes) {
190 unsigned int checksum = 0x0;
191 for (int i = 0; i < q->payload_bytes / sizeof(unsigned int); ++i) {
192 checksum ^= ((unsigned int *)buffer)[i];
194 printf("Checksum: %08x\n", checksum);
196 if ((pay_qty = write(pay_fd, buffer, q->payload_bytes)) != q->payload_bytes)
197 fprintf(stderr, "Error: whilst writing payload expected %d bytes but wrote %ld\n", q->payload_bytes, pay_qty);
199 fprintf(stderr, "Error: whilst reading payload expected %d bytes but read %ld\n", q->payload_bytes, z_qty);
201 if(utime(filepath, &filetime) == -1) {
202 fprintf(stderr, "Error: unable to set timestamp on '%s' (%d) ", filepath, errno);
206 if (q->payload_bytes >= 4) {
207 uint8_t comp_type = *(uint8_t *)buffer;
208 uint8_t dict_size = *(uint8_t *)(buffer + 1);
209 if (comp_type >= IMPLODE_BINARY && comp_type <= IMPLODE_ASCII && dict_size >= IMPLODE_DICT_1K && dict_size <= IMPLODE_DICT_4K) {
210 void * buffer_decomp;
211 if ((buffer_decomp = calloc(q->file_bytes, 1)) != 0) {
212 uint32_t decomp_bytes = q->file_bytes;
213 if (explode(buffer, q->payload_bytes, buffer_decomp, &decomp_bytes)) {
214 if (decomp_bytes > q->file_bytes) {
215 fprintf(stderr, "Expected %d uncompressed bytes but got %d\n", q->file_bytes, decomp_bytes);
218 if ((decomp_fd = openat(dir_fd, q->name, O_WRONLY | O_CREAT | O_TRUNC, 0644))) {
221 printf("Creating uncompressed file '%s/%s'\n",dir_name, q->name);
222 if ((decomp_qty = write(decomp_fd, buffer_decomp, q->file_bytes)) != q->file_bytes)
223 fprintf(stderr, "Error: whilst writing decompressed data expected %d bytes but wrote %ld\n", q->file_bytes, decomp_qty);
225 filepath[strlen(filepath) - 2] = 0;
226 if(utime(filepath, &filetime) == -1) {
227 fprintf(stderr, "Error: unable to set timestamp on '%s' (%d) ", filepath, errno);
232 fprintf(stderr, "Error: failed to decompress '%s', payload: %d decompressed: %d\n", q->name, q->payload_bytes, decomp_bytes);
236 fprintf(stderr, "Error: unable to allocate %d bytes for decompression buffer\n", q->file_bytes);
239 fprintf(stderr, "Does not look like TTComp compressed data. header: %04x\n", *(uint16_t *)buffer);
242 fprintf(stderr, "Error: payload %d bytes smaller than minimum allowed TTComp of 4\n", q->payload_bytes);
247 fprintf(stderr, "Error %d: failed to create %s\n", errno, z_filename);
251 fprintf(stderr, "Error %d: cannot open directory at (%d) '%s': ", errno, dir_fd, dir_name);
255 q = (void *)q + q->entry_bytes;
261 fprintf(stderr, "Error: %s\n", "failed to seek or couldn't allocate memory for directories");
270 main(int argc, char **argv, char **env)
280 for (char **e = env; *e; ++e) {
281 if (strncmp("DEBUG", *e, 5) == 0)
284 for (char **a = argv; *a; ++a) {
285 if (debug) printf("Arg: %s\n", *a);
286 if (strncmp("-x", *a, 2) == 0 || strncmp("--extract", *a, 9) == 0)
290 if ((fd = open(argv[1], O_RDONLY)) != -1) {
291 result = headers_decode(fd);
294 fprintf(stderr, "Error: could not open %s\n", argv[0]);