├── Makefile ├── README.md ├── blast.c ├── blast.h ├── d2kinst.nsi └── unZ.c /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-m32 -static-libgcc -Os 3 | 4 | all: 5 | $(CC) $(CFLAGS) blast.c unZ.c -o unZ 6 | 7 | clean: 8 | rm -f unZ.exe -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # d2kinst 2 | Simple, small installer for Dune 2000 3 | -------------------------------------------------------------------------------- /blast.c: -------------------------------------------------------------------------------- 1 | /* blast.c 2 | * Copyright (C) 2003, 2012 Mark Adler 3 | * For conditions of distribution and use, see copyright notice in blast.h 4 | * version 1.2, 24 Oct 2012 5 | * 6 | * blast.c decompresses data compressed by the PKWare Compression Library. 7 | * This function provides functionality similar to the explode() function of 8 | * the PKWare library, hence the name "blast". 9 | * 10 | * This decompressor is based on the excellent format description provided by 11 | * Ben Rudiak-Gould in comp.compression on August 13, 2001. Interestingly, the 12 | * example Ben provided in the post is incorrect. The distance 110001 should 13 | * instead be 111000. When corrected, the example byte stream becomes: 14 | * 15 | * 00 04 82 24 25 8f 80 7f 16 | * 17 | * which decompresses to "AIAIAIAIAIAIA" (without the quotes). 18 | */ 19 | 20 | /* 21 | * Change history: 22 | * 23 | * 1.0 12 Feb 2003 - First version 24 | * 1.1 16 Feb 2003 - Fixed distance check for > 4 GB uncompressed data 25 | * 1.2 24 Oct 2012 - Add note about using binary mode in stdio 26 | * - Fix comparisons of differently signed integers 27 | */ 28 | 29 | #include /* for setjmp(), longjmp(), and jmp_buf */ 30 | #include "blast.h" /* prototype for blast() */ 31 | 32 | #define local static /* for local function definitions */ 33 | #define MAXBITS 13 /* maximum code length */ 34 | #define MAXWIN 4096 /* maximum window size */ 35 | 36 | /* input and output state */ 37 | struct state { 38 | /* input state */ 39 | blast_in infun; /* input function provided by user */ 40 | void *inhow; /* opaque information passed to infun() */ 41 | unsigned char *in; /* next input location */ 42 | unsigned left; /* available input at in */ 43 | int bitbuf; /* bit buffer */ 44 | int bitcnt; /* number of bits in bit buffer */ 45 | 46 | /* input limit error return state for bits() and decode() */ 47 | jmp_buf env; 48 | 49 | /* output state */ 50 | blast_out outfun; /* output function provided by user */ 51 | void *outhow; /* opaque information passed to outfun() */ 52 | unsigned next; /* index of next write location in out[] */ 53 | int first; /* true to check distances (for first 4K) */ 54 | unsigned char out[MAXWIN]; /* output buffer and sliding window */ 55 | }; 56 | 57 | /* 58 | * Return need bits from the input stream. This always leaves less than 59 | * eight bits in the buffer. bits() works properly for need == 0. 60 | * 61 | * Format notes: 62 | * 63 | * - Bits are stored in bytes from the least significant bit to the most 64 | * significant bit. Therefore bits are dropped from the bottom of the bit 65 | * buffer, using shift right, and new bytes are appended to the top of the 66 | * bit buffer, using shift left. 67 | */ 68 | local int bits(struct state *s, int need) 69 | { 70 | int val; /* bit accumulator */ 71 | 72 | /* load at least need bits into val */ 73 | val = s->bitbuf; 74 | while (s->bitcnt < need) { 75 | if (s->left == 0) { 76 | s->left = s->infun(s->inhow, &(s->in)); 77 | if (s->left == 0) longjmp(s->env, 1); /* out of input */ 78 | } 79 | val |= (int)(*(s->in)++) << s->bitcnt; /* load eight bits */ 80 | s->left--; 81 | s->bitcnt += 8; 82 | } 83 | 84 | /* drop need bits and update buffer, always zero to seven bits left */ 85 | s->bitbuf = val >> need; 86 | s->bitcnt -= need; 87 | 88 | /* return need bits, zeroing the bits above that */ 89 | return val & ((1 << need) - 1); 90 | } 91 | 92 | /* 93 | * Huffman code decoding tables. count[1..MAXBITS] is the number of symbols of 94 | * each length, which for a canonical code are stepped through in order. 95 | * symbol[] are the symbol values in canonical order, where the number of 96 | * entries is the sum of the counts in count[]. The decoding process can be 97 | * seen in the function decode() below. 98 | */ 99 | struct huffman { 100 | short *count; /* number of symbols of each length */ 101 | short *symbol; /* canonically ordered symbols */ 102 | }; 103 | 104 | /* 105 | * Decode a code from the stream s using huffman table h. Return the symbol or 106 | * a negative value if there is an error. If all of the lengths are zero, i.e. 107 | * an empty code, or if the code is incomplete and an invalid code is received, 108 | * then -9 is returned after reading MAXBITS bits. 109 | * 110 | * Format notes: 111 | * 112 | * - The codes as stored in the compressed data are bit-reversed relative to 113 | * a simple integer ordering of codes of the same lengths. Hence below the 114 | * bits are pulled from the compressed data one at a time and used to 115 | * build the code value reversed from what is in the stream in order to 116 | * permit simple integer comparisons for decoding. 117 | * 118 | * - The first code for the shortest length is all ones. Subsequent codes of 119 | * the same length are simply integer decrements of the previous code. When 120 | * moving up a length, a one bit is appended to the code. For a complete 121 | * code, the last code of the longest length will be all zeros. To support 122 | * this ordering, the bits pulled during decoding are inverted to apply the 123 | * more "natural" ordering starting with all zeros and incrementing. 124 | */ 125 | local int decode(struct state *s, struct huffman *h) 126 | { 127 | int len; /* current number of bits in code */ 128 | int code; /* len bits being decoded */ 129 | int first; /* first code of length len */ 130 | int count; /* number of codes of length len */ 131 | int index; /* index of first code of length len in symbol table */ 132 | int bitbuf; /* bits from stream */ 133 | int left; /* bits left in next or left to process */ 134 | short *next; /* next number of codes */ 135 | 136 | bitbuf = s->bitbuf; 137 | left = s->bitcnt; 138 | code = first = index = 0; 139 | len = 1; 140 | next = h->count + 1; 141 | while (1) { 142 | while (left--) { 143 | code |= (bitbuf & 1) ^ 1; /* invert code */ 144 | bitbuf >>= 1; 145 | count = *next++; 146 | if (code < first + count) { /* if length len, return symbol */ 147 | s->bitbuf = bitbuf; 148 | s->bitcnt = (s->bitcnt - len) & 7; 149 | return h->symbol[index + (code - first)]; 150 | } 151 | index += count; /* else update for next length */ 152 | first += count; 153 | first <<= 1; 154 | code <<= 1; 155 | len++; 156 | } 157 | left = (MAXBITS+1) - len; 158 | if (left == 0) break; 159 | if (s->left == 0) { 160 | s->left = s->infun(s->inhow, &(s->in)); 161 | if (s->left == 0) longjmp(s->env, 1); /* out of input */ 162 | } 163 | bitbuf = *(s->in)++; 164 | s->left--; 165 | if (left > 8) left = 8; 166 | } 167 | return -9; /* ran out of codes */ 168 | } 169 | 170 | /* 171 | * Given a list of repeated code lengths rep[0..n-1], where each byte is a 172 | * count (high four bits + 1) and a code length (low four bits), generate the 173 | * list of code lengths. This compaction reduces the size of the object code. 174 | * Then given the list of code lengths length[0..n-1] representing a canonical 175 | * Huffman code for n symbols, construct the tables required to decode those 176 | * codes. Those tables are the number of codes of each length, and the symbols 177 | * sorted by length, retaining their original order within each length. The 178 | * return value is zero for a complete code set, negative for an over- 179 | * subscribed code set, and positive for an incomplete code set. The tables 180 | * can be used if the return value is zero or positive, but they cannot be used 181 | * if the return value is negative. If the return value is zero, it is not 182 | * possible for decode() using that table to return an error--any stream of 183 | * enough bits will resolve to a symbol. If the return value is positive, then 184 | * it is possible for decode() using that table to return an error for received 185 | * codes past the end of the incomplete lengths. 186 | */ 187 | local int construct(struct huffman *h, const unsigned char *rep, int n) 188 | { 189 | int symbol; /* current symbol when stepping through length[] */ 190 | int len; /* current length when stepping through h->count[] */ 191 | int left; /* number of possible codes left of current length */ 192 | short offs[MAXBITS+1]; /* offsets in symbol table for each length */ 193 | short length[256]; /* code lengths */ 194 | 195 | /* convert compact repeat counts into symbol bit length list */ 196 | symbol = 0; 197 | do { 198 | len = *rep++; 199 | left = (len >> 4) + 1; 200 | len &= 15; 201 | do { 202 | length[symbol++] = len; 203 | } while (--left); 204 | } while (--n); 205 | n = symbol; 206 | 207 | /* count number of codes of each length */ 208 | for (len = 0; len <= MAXBITS; len++) 209 | h->count[len] = 0; 210 | for (symbol = 0; symbol < n; symbol++) 211 | (h->count[length[symbol]])++; /* assumes lengths are within bounds */ 212 | if (h->count[0] == n) /* no codes! */ 213 | return 0; /* complete, but decode() will fail */ 214 | 215 | /* check for an over-subscribed or incomplete set of lengths */ 216 | left = 1; /* one possible code of zero length */ 217 | for (len = 1; len <= MAXBITS; len++) { 218 | left <<= 1; /* one more bit, double codes left */ 219 | left -= h->count[len]; /* deduct count from possible codes */ 220 | if (left < 0) return left; /* over-subscribed--return negative */ 221 | } /* left > 0 means incomplete */ 222 | 223 | /* generate offsets into symbol table for each length for sorting */ 224 | offs[1] = 0; 225 | for (len = 1; len < MAXBITS; len++) 226 | offs[len + 1] = offs[len] + h->count[len]; 227 | 228 | /* 229 | * put symbols in table sorted by length, by symbol order within each 230 | * length 231 | */ 232 | for (symbol = 0; symbol < n; symbol++) 233 | if (length[symbol] != 0) 234 | h->symbol[offs[length[symbol]]++] = symbol; 235 | 236 | /* return zero for complete set, positive for incomplete set */ 237 | return left; 238 | } 239 | 240 | /* 241 | * Decode PKWare Compression Library stream. 242 | * 243 | * Format notes: 244 | * 245 | * - First byte is 0 if literals are uncoded or 1 if they are coded. Second 246 | * byte is 4, 5, or 6 for the number of extra bits in the distance code. 247 | * This is the base-2 logarithm of the dictionary size minus six. 248 | * 249 | * - Compressed data is a combination of literals and length/distance pairs 250 | * terminated by an end code. Literals are either Huffman coded or 251 | * uncoded bytes. A length/distance pair is a coded length followed by a 252 | * coded distance to represent a string that occurs earlier in the 253 | * uncompressed data that occurs again at the current location. 254 | * 255 | * - A bit preceding a literal or length/distance pair indicates which comes 256 | * next, 0 for literals, 1 for length/distance. 257 | * 258 | * - If literals are uncoded, then the next eight bits are the literal, in the 259 | * normal bit order in th stream, i.e. no bit-reversal is needed. Similarly, 260 | * no bit reversal is needed for either the length extra bits or the distance 261 | * extra bits. 262 | * 263 | * - Literal bytes are simply written to the output. A length/distance pair is 264 | * an instruction to copy previously uncompressed bytes to the output. The 265 | * copy is from distance bytes back in the output stream, copying for length 266 | * bytes. 267 | * 268 | * - Distances pointing before the beginning of the output data are not 269 | * permitted. 270 | * 271 | * - Overlapped copies, where the length is greater than the distance, are 272 | * allowed and common. For example, a distance of one and a length of 518 273 | * simply copies the last byte 518 times. A distance of four and a length of 274 | * twelve copies the last four bytes three times. A simple forward copy 275 | * ignoring whether the length is greater than the distance or not implements 276 | * this correctly. 277 | */ 278 | local int decomp(struct state *s) 279 | { 280 | int lit; /* true if literals are coded */ 281 | int dict; /* log2(dictionary size) - 6 */ 282 | int symbol; /* decoded symbol, extra bits for distance */ 283 | int len; /* length for copy */ 284 | unsigned dist; /* distance for copy */ 285 | int copy; /* copy counter */ 286 | unsigned char *from, *to; /* copy pointers */ 287 | static int virgin = 1; /* build tables once */ 288 | static short litcnt[MAXBITS+1], litsym[256]; /* litcode memory */ 289 | static short lencnt[MAXBITS+1], lensym[16]; /* lencode memory */ 290 | static short distcnt[MAXBITS+1], distsym[64]; /* distcode memory */ 291 | static struct huffman litcode = {litcnt, litsym}; /* length code */ 292 | static struct huffman lencode = {lencnt, lensym}; /* length code */ 293 | static struct huffman distcode = {distcnt, distsym};/* distance code */ 294 | /* bit lengths of literal codes */ 295 | static const unsigned char litlen[] = { 296 | 11, 124, 8, 7, 28, 7, 188, 13, 76, 4, 10, 8, 12, 10, 12, 10, 8, 23, 8, 297 | 9, 7, 6, 7, 8, 7, 6, 55, 8, 23, 24, 12, 11, 7, 9, 11, 12, 6, 7, 22, 5, 298 | 7, 24, 6, 11, 9, 6, 7, 22, 7, 11, 38, 7, 9, 8, 25, 11, 8, 11, 9, 12, 299 | 8, 12, 5, 38, 5, 38, 5, 11, 7, 5, 6, 21, 6, 10, 53, 8, 7, 24, 10, 27, 300 | 44, 253, 253, 253, 252, 252, 252, 13, 12, 45, 12, 45, 12, 61, 12, 45, 301 | 44, 173}; 302 | /* bit lengths of length codes 0..15 */ 303 | static const unsigned char lenlen[] = {2, 35, 36, 53, 38, 23}; 304 | /* bit lengths of distance codes 0..63 */ 305 | static const unsigned char distlen[] = {2, 20, 53, 230, 247, 151, 248}; 306 | static const short base[16] = { /* base for length codes */ 307 | 3, 2, 4, 5, 6, 7, 8, 9, 10, 12, 16, 24, 40, 72, 136, 264}; 308 | static const char extra[16] = { /* extra bits for length codes */ 309 | 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8}; 310 | 311 | /* set up decoding tables (once--might not be thread-safe) */ 312 | if (virgin) { 313 | construct(&litcode, litlen, sizeof(litlen)); 314 | construct(&lencode, lenlen, sizeof(lenlen)); 315 | construct(&distcode, distlen, sizeof(distlen)); 316 | virgin = 0; 317 | } 318 | 319 | /* read header */ 320 | lit = bits(s, 8); 321 | if (lit > 1) return -1; 322 | dict = bits(s, 8); 323 | if (dict < 4 || dict > 6) return -2; 324 | 325 | /* decode literals and length/distance pairs */ 326 | do { 327 | if (bits(s, 1)) { 328 | /* get length */ 329 | symbol = decode(s, &lencode); 330 | len = base[symbol] + bits(s, extra[symbol]); 331 | if (len == 519) break; /* end code */ 332 | 333 | /* get distance */ 334 | symbol = len == 2 ? 2 : dict; 335 | dist = decode(s, &distcode) << symbol; 336 | dist += bits(s, symbol); 337 | dist++; 338 | if (s->first && dist > s->next) 339 | return -3; /* distance too far back */ 340 | 341 | /* copy length bytes from distance bytes back */ 342 | do { 343 | to = s->out + s->next; 344 | from = to - dist; 345 | copy = MAXWIN; 346 | if (s->next < dist) { 347 | from += copy; 348 | copy = dist; 349 | } 350 | copy -= s->next; 351 | if (copy > len) copy = len; 352 | len -= copy; 353 | s->next += copy; 354 | do { 355 | *to++ = *from++; 356 | } while (--copy); 357 | if (s->next == MAXWIN) { 358 | if (s->outfun(s->outhow, s->out, s->next)) return 1; 359 | s->next = 0; 360 | s->first = 0; 361 | } 362 | } while (len != 0); 363 | } 364 | else { 365 | /* get literal and write it */ 366 | symbol = lit ? decode(s, &litcode) : bits(s, 8); 367 | s->out[s->next++] = symbol; 368 | if (s->next == MAXWIN) { 369 | if (s->outfun(s->outhow, s->out, s->next)) return 1; 370 | s->next = 0; 371 | s->first = 0; 372 | } 373 | } 374 | } while (1); 375 | return 0; 376 | } 377 | 378 | /* See comments in blast.h */ 379 | int blast(blast_in infun, void *inhow, blast_out outfun, void *outhow) 380 | { 381 | struct state s; /* input/output state */ 382 | int err; /* return value */ 383 | 384 | /* initialize input state */ 385 | s.infun = infun; 386 | s.inhow = inhow; 387 | s.left = 0; 388 | s.bitbuf = 0; 389 | s.bitcnt = 0; 390 | 391 | /* initialize output state */ 392 | s.outfun = outfun; 393 | s.outhow = outhow; 394 | s.next = 0; 395 | s.first = 1; 396 | 397 | /* return if bits() or decode() tries to read past available input */ 398 | if (setjmp(s.env) != 0) /* if came back here via longjmp(), */ 399 | err = 2; /* then skip decomp(), return error */ 400 | else 401 | err = decomp(&s); /* decompress */ 402 | 403 | /* write any leftover output and update the error code if needed */ 404 | if (err != 1 && s.next && s.outfun(s.outhow, s.out, s.next) && err == 0) 405 | err = 1; 406 | return err; 407 | } 408 | 409 | #ifdef TEST 410 | /* Example of how to use blast() */ 411 | #include 412 | #include 413 | 414 | #define CHUNK 16384 415 | 416 | local unsigned inf(void *how, unsigned char **buf) 417 | { 418 | static unsigned char hold[CHUNK]; 419 | 420 | *buf = hold; 421 | return fread(hold, 1, CHUNK, (FILE *)how); 422 | } 423 | 424 | local int outf(void *how, unsigned char *buf, unsigned len) 425 | { 426 | return fwrite(buf, 1, len, (FILE *)how) != len; 427 | } 428 | 429 | /* Decompress a PKWare Compression Library stream from stdin to stdout */ 430 | int main(void) 431 | { 432 | int ret, n; 433 | 434 | /* decompress to stdout */ 435 | ret = blast(inf, stdin, outf, stdout); 436 | if (ret != 0) fprintf(stderr, "blast error: %d\n", ret); 437 | 438 | /* see if there are any leftover bytes */ 439 | n = 0; 440 | while (getchar() != EOF) n++; 441 | if (n) fprintf(stderr, "blast warning: %d unused bytes of input\n", n); 442 | 443 | /* return blast() error code */ 444 | return ret; 445 | } 446 | #endif 447 | -------------------------------------------------------------------------------- /blast.h: -------------------------------------------------------------------------------- 1 | /* blast.h -- interface for blast.c 2 | Copyright (C) 2003, 2012 Mark Adler 3 | version 1.2, 24 Oct 2012 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the author be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgment in the product documentation would be 16 | appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 3. This notice may not be removed or altered from any source distribution. 20 | 21 | Mark Adler madler@alumni.caltech.edu 22 | */ 23 | 24 | 25 | /* 26 | * blast() decompresses the PKWare Data Compression Library (DCL) compressed 27 | * format. It provides the same functionality as the explode() function in 28 | * that library. (Note: PKWare overused the "implode" verb, and the format 29 | * used by their library implode() function is completely different and 30 | * incompatible with the implode compression method supported by PKZIP.) 31 | * 32 | * The binary mode for stdio functions should be used to assure that the 33 | * compressed data is not corrupted when read or written. For example: 34 | * fopen(..., "rb") and fopen(..., "wb"). 35 | */ 36 | 37 | 38 | typedef unsigned (*blast_in)(void *how, unsigned char **buf); 39 | typedef int (*blast_out)(void *how, unsigned char *buf, unsigned len); 40 | /* Definitions for input/output functions passed to blast(). See below for 41 | * what the provided functions need to do. 42 | */ 43 | 44 | 45 | int blast(blast_in infun, void *inhow, blast_out outfun, void *outhow); 46 | /* Decompress input to output using the provided infun() and outfun() calls. 47 | * On success, the return value of blast() is zero. If there is an error in 48 | * the source data, i.e. it is not in the proper format, then a negative value 49 | * is returned. If there is not enough input available or there is not enough 50 | * output space, then a positive error is returned. 51 | * 52 | * The input function is invoked: len = infun(how, &buf), where buf is set by 53 | * infun() to point to the input buffer, and infun() returns the number of 54 | * available bytes there. If infun() returns zero, then blast() returns with 55 | * an input error. (blast() only asks for input if it needs it.) inhow is for 56 | * use by the application to pass an input descriptor to infun(), if desired. 57 | * 58 | * The output function is invoked: err = outfun(how, buf, len), where the bytes 59 | * to be written are buf[0..len-1]. If err is not zero, then blast() returns 60 | * with an output error. outfun() is always called with len <= 4096. outhow 61 | * is for use by the application to pass an output descriptor to outfun(), if 62 | * desired. 63 | * 64 | * The return codes are: 65 | * 66 | * 2: ran out of input before completing decompression 67 | * 1: output error before completing decompression 68 | * 0: successful decompression 69 | * -1: literal flag not zero or one 70 | * -2: dictionary size not in 4..6 71 | * -3: distance is too far back 72 | * 73 | * At the bottom of blast.c is an example program that uses blast() that can be 74 | * compiled to produce a command-line decompression filter by defining TEST. 75 | */ 76 | -------------------------------------------------------------------------------- /d2kinst.nsi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmileTheory/d2kinst/b1f28c351bb686019217a6cc2ff209a116498435/d2kinst.nsi -------------------------------------------------------------------------------- /unZ.c: -------------------------------------------------------------------------------- 1 | // file format gleaned from https://github.com/OpenRA/OpenRA/blob/629fe95ebdfd669d04af5cdbdb900bf09e08e39e/OpenRA.FileFormats/Filesystem/InstallShieldPackage.cs 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "blast.h" 11 | 12 | typedef struct direntry_s 13 | { 14 | char *name; 15 | int numFiles; 16 | } 17 | direntry_t; 18 | 19 | typedef struct fchunk_s 20 | { 21 | FILE *fp; 22 | unsigned int size; 23 | } 24 | fchunk_t; 25 | 26 | #if defined(SHOWTIME) 27 | char *months[12] = 28 | { 29 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", 30 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 31 | }; 32 | #endif 33 | 34 | #define GET_UI32(x) ((x)[0] | ((x)[1] << 8) | ((x)[2] << 16) | ((x)[3] << 24)) 35 | #define GET_UI16(x) ((x)[0] | ((x)[1] << 8)) 36 | 37 | unsigned inf(void *how, unsigned char **buf) 38 | { 39 | static unsigned char hold[16384]; 40 | fchunk_t *fc = how; 41 | int numBytes; 42 | 43 | *buf = hold; 44 | 45 | numBytes = fc->size; 46 | if (numBytes == 0) return -1; 47 | if (numBytes > 16384) numBytes = 16384; 48 | 49 | fc->size -= numBytes; 50 | 51 | return fread(hold, 1, numBytes, fc->fp); 52 | } 53 | 54 | int outf(void *how, unsigned char *buf, unsigned len) 55 | { 56 | return fwrite(buf, 1, len, (FILE *)how) != len; 57 | } 58 | 59 | int unZ(char *archiveFilename, char *extractPath) 60 | { 61 | FILE *fp; 62 | fchunk_t fc; 63 | 64 | unsigned int i, destsize; 65 | unsigned char header[256], *toc; 66 | 67 | unsigned int numFiles, compsize, tocOffset, tocSize, numDirs; 68 | direntry_t *dirs; 69 | 70 | destsize = strlen(extractPath); 71 | 72 | fp = fopen(archiveFilename, "rb"); 73 | 74 | if (!fp) 75 | { 76 | printf("Error opening %s!\n", archiveFilename); 77 | return; 78 | } 79 | 80 | fseek(fp, 0, SEEK_END); 81 | compsize = ftell(fp); 82 | fseek(fp, 0, SEEK_SET); 83 | 84 | fread(header, 256, 1, fp); 85 | 86 | // check signature 87 | if (GET_UI32(header) != 0x8C655D13) 88 | { 89 | printf("Archive corrupt: Bad signature!\n"); 90 | return 0; 91 | } 92 | 93 | //numFiles = GET_UI16(header + 12); 94 | 95 | // check size 96 | if (compsize != GET_UI32(header + 18)) 97 | { 98 | printf("Archive corrupt: Incorrect size!\n"); 99 | return 0; 100 | } 101 | 102 | // read toc 103 | tocOffset = GET_UI32(header + 41); 104 | tocSize = compsize - tocOffset; 105 | 106 | toc = malloc(tocSize); 107 | fseek(fp, tocOffset, SEEK_SET); 108 | fread(toc, 1, tocSize, fp); 109 | tocOffset = 0; 110 | 111 | 112 | // parse dirs 113 | numDirs = GET_UI16(header + 49); 114 | dirs = malloc(sizeof(*dirs) * numDirs); 115 | 116 | for (i = 0; i < numDirs; i++) 117 | { 118 | unsigned int dir_numFiles = GET_UI16(toc + tocOffset); 119 | unsigned int dir_entrySize = GET_UI16(toc + tocOffset + 2); 120 | unsigned int dir_nameSize = GET_UI16(toc + tocOffset + 4); 121 | 122 | dirs[i].name = malloc(destsize + 1 + dir_nameSize + 1); 123 | strcpy(dirs[i].name, extractPath); 124 | dirs[i].name[destsize] = '\\'; 125 | memcpy(dirs[i].name + destsize + 1, toc + tocOffset + 6, dir_nameSize); 126 | dirs[i].name[destsize + 1 + dir_nameSize] = '\0'; 127 | dirs[i].numFiles = dir_numFiles; 128 | 129 | tocOffset += dir_entrySize; 130 | } 131 | 132 | // parse and save files 133 | fseek(fp, 255, SEEK_SET); 134 | fc.fp = fp; 135 | for (i = 0; i < numDirs; i++) 136 | { 137 | unsigned int dir_nameSize; 138 | int j; 139 | 140 | _mkdir(extractPath); 141 | if (dirs[i].name[0]) 142 | _mkdir(dirs[i].name); 143 | 144 | printf("Dir %s, %d files\n", dirs[i].name, dirs[i].numFiles); 145 | dir_nameSize = strlen(dirs[i].name); 146 | 147 | for (j = 0; j < dirs[i].numFiles; j++) 148 | { 149 | FILE *fpw; 150 | int blastErr; 151 | 152 | unsigned int file_decompressedSize = GET_UI32(toc + tocOffset + 3); 153 | unsigned int file_compressedSize = GET_UI32(toc + tocOffset + 7); 154 | unsigned int file_modifiedDate = GET_UI16(toc + tocOffset + 15); 155 | unsigned int file_modifiedTime = GET_UI16(toc + tocOffset + 17); 156 | unsigned int file_entrySize = GET_UI16(toc + tocOffset + 23); 157 | unsigned int file_nameSize = toc[tocOffset + 29]; 158 | 159 | char *filename; 160 | 161 | struct tm tmm; 162 | struct _utimbuf ut; 163 | 164 | filename = malloc(dir_nameSize + file_nameSize + 2); 165 | strcpy(filename, dirs[i].name); 166 | filename[dir_nameSize] = '\\'; 167 | strncpy(filename + dir_nameSize + 1, toc + tocOffset + 30, file_nameSize); 168 | filename[dir_nameSize + 1 + file_nameSize] = '\0'; 169 | 170 | printf("%s, %d bytes, %.3f%%\n", filename, file_decompressedSize, (float)file_compressedSize / file_decompressedSize * 100.0f); 171 | 172 | memset(&tmm, 0, sizeof(tmm)); 173 | tmm.tm_year = ( file_modifiedDate >> 9) + 80; 174 | tmm.tm_mon = ((file_modifiedDate & 0x01E0) >> 5) - 1; 175 | tmm.tm_mday = file_modifiedDate & 0x001F; 176 | tmm.tm_hour = file_modifiedTime >> 11; 177 | tmm.tm_min = (file_modifiedTime & 0x07E0) >> 5; 178 | tmm.tm_sec = (file_modifiedTime & 0x001F) * 2; 179 | 180 | #if defined(SHOWTIME) 181 | printf("last modified %d %s %d %02d:%02d:%02d\n", 182 | tmm.tm_year + 1900, months[tmm.tm_mon], tmm.tm_mday, tmm.tm_hour, tmm.tm_min, tmm.tm_sec); 183 | #endif 184 | 185 | tocOffset += file_entrySize; 186 | 187 | fpw = fopen(filename, "wb"); 188 | fc.size = file_compressedSize; 189 | 190 | blastErr = blast(inf, &fc, outf, fpw); 191 | 192 | if (blastErr == 1) 193 | printf("Error writing file!\n"); 194 | else if (blastErr != 0) 195 | printf("Archive corrupt: Error decompressing, trying to continue"); 196 | 197 | fclose(fpw); 198 | 199 | ut.actime = time(NULL); 200 | ut.modtime = mktime(&tmm); 201 | _utime(filename, &ut); 202 | 203 | free(filename); 204 | } 205 | } 206 | 207 | fclose(fp); 208 | 209 | for (i = 0; i < numDirs; i++) 210 | free(dirs[i].name); 211 | 212 | free(dirs); 213 | free(toc); 214 | 215 | return 0; 216 | } 217 | 218 | int main(int argc, char *argv[]) 219 | { 220 | if (argc != 3) 221 | { 222 | printf("Usage:\n %s \n", argv[0]); 223 | return 0; 224 | } 225 | 226 | return unZ(argv[1], argv[2]); 227 | } 228 | --------------------------------------------------------------------------------