From 12ee80de00efa95ae9430500d33fecfabbb5d5bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9=20van=20Geldorp?= Date: Fri, 31 Dec 2004 14:01:01 +0000 Subject: [PATCH] Merge Wine-20041201 svn path=/trunk/; revision=12436 --- reactos/lib/cabinet/Makefile.in | 18 + reactos/lib/cabinet/cabextract.c | 2654 ++++++++++++++++++++++++++ reactos/lib/cabinet/cabinet.h | 580 ++++++ reactos/lib/cabinet/cabinet.rc | 28 + reactos/lib/cabinet/cabinet.spec | 14 + reactos/lib/cabinet/cabinet_main.c | 133 ++ reactos/lib/cabinet/fci.c | 127 ++ reactos/lib/cabinet/fdi.c | 2839 ++++++++++++++++++++++++++++ 8 files changed, 6393 insertions(+) create mode 100644 reactos/lib/cabinet/Makefile.in create mode 100644 reactos/lib/cabinet/cabextract.c create mode 100644 reactos/lib/cabinet/cabinet.h create mode 100644 reactos/lib/cabinet/cabinet.rc create mode 100644 reactos/lib/cabinet/cabinet.spec create mode 100644 reactos/lib/cabinet/cabinet_main.c create mode 100644 reactos/lib/cabinet/fci.c create mode 100644 reactos/lib/cabinet/fdi.c diff --git a/reactos/lib/cabinet/Makefile.in b/reactos/lib/cabinet/Makefile.in new file mode 100644 index 00000000000..618aa04d5dc --- /dev/null +++ b/reactos/lib/cabinet/Makefile.in @@ -0,0 +1,18 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = cabinet.dll +IMPORTS = kernel32 + +C_SRCS = \ + cabextract.c \ + cabinet_main.c \ + fci.c \ + fdi.c + +RC_SRCS = cabinet.rc + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/reactos/lib/cabinet/cabextract.c b/reactos/lib/cabinet/cabextract.c new file mode 100644 index 00000000000..f20eb10454e --- /dev/null +++ b/reactos/lib/cabinet/cabextract.c @@ -0,0 +1,2654 @@ +/* + * cabextract.c + * + * Copyright 2000-2002 Stuart Caie + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Principal author: Stuart Caie + * + * Based on specification documents from Microsoft Corporation + * Quantum decompression researched and implemented by Matthew Russoto + * Huffman code adapted from unlzx by Dave Tritscher. + * InfoZip team's INFLATE implementation adapted to MSZIP by Dirk Stoecker. + * Major LZX fixes by Jae Jung. + */ + +#include "config.h" + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" + +#include "cabinet.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(cabinet); + +THOSE_ZIP_CONSTS; + +/* all the file IO is abstracted into these routines: + * cabinet_(open|close|read|seek|skip|getoffset) + * file_(open|close|write) + */ + +/* try to open a cabinet file, returns success */ +BOOL cabinet_open(struct cabinet *cab) +{ + const char *name = cab->filename; + HANDLE fh; + + TRACE("(cab == ^%p)\n", cab); + + if ((fh = CreateFileA( name, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL )) == INVALID_HANDLE_VALUE) { + ERR("Couldn't open %s\n", debugstr_a(name)); + return FALSE; + } + + /* seek to end of file and get the length */ + if ((cab->filelen = SetFilePointer(fh, 0, NULL, FILE_END)) == INVALID_SET_FILE_POINTER) { + if (GetLastError() != NO_ERROR) { + ERR("Seek END failed: %s\n", debugstr_a(name)); + CloseHandle(fh); + return FALSE; + } + } + + /* return to the start of the file */ + if (SetFilePointer(fh, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { + ERR("Seek BEGIN failed: %s\n", debugstr_a(name)); + CloseHandle(fh); + return FALSE; + } + + cab->fh = fh; + return TRUE; +} + +/******************************************************************* + * cabinet_close (internal) + * + * close the file handle in a struct cabinet. + */ +void cabinet_close(struct cabinet *cab) { + TRACE("(cab == ^%p)\n", cab); + if (cab->fh) CloseHandle(cab->fh); + cab->fh = 0; +} + +/******************************************************* + * ensure_filepath2 (internal) + */ +BOOL ensure_filepath2(char *path) { + BOOL ret = TRUE; + int len; + char *new_path; + + new_path = HeapAlloc(GetProcessHeap(), 0, (strlen(path) + 1)); + strcpy(new_path, path); + + while((len = strlen(new_path)) && new_path[len - 1] == '\\') + new_path[len - 1] = 0; + + TRACE("About to try to create directory %s\n", debugstr_a(new_path)); + while(!CreateDirectoryA(new_path, NULL)) { + char *slash; + DWORD last_error = GetLastError(); + + if(last_error == ERROR_ALREADY_EXISTS) + break; + + if(last_error != ERROR_PATH_NOT_FOUND) { + ret = FALSE; + break; + } + + if(!(slash = strrchr(new_path, '\\'))) { + ret = FALSE; + break; + } + + len = slash - new_path; + new_path[len] = 0; + if(! ensure_filepath2(new_path)) { + ret = FALSE; + break; + } + new_path[len] = '\\'; + TRACE("New path in next iteration: %s\n", debugstr_a(new_path)); + } + + HeapFree(GetProcessHeap(), 0, new_path); + return ret; +} + + +/********************************************************************** + * ensure_filepath (internal) + * + * ensure_filepath("a\b\c\d.txt") ensures a, a\b and a\b\c exist as dirs + */ +BOOL ensure_filepath(char *path) { + char new_path[MAX_PATH]; + int len, i, lastslashpos = -1; + + TRACE("(path == %s)\n", debugstr_a(path)); + + strcpy(new_path, path); + /* remove trailing slashes (shouldn't need to but wth...) */ + while ((len = strlen(new_path)) && new_path[len - 1] == '\\') + new_path[len - 1] = 0; + /* find the position of the last '\\' */ + for (i=0; i 0) { + new_path[lastslashpos] = 0; + /* may be trailing slashes but ensure_filepath2 will chop them */ + return ensure_filepath2(new_path); + } else + return TRUE; /* ? */ +} + +/******************************************************************* + * file_open (internal) + * + * opens a file for output, returns success + */ +BOOL file_open(struct cab_file *fi, BOOL lower, LPCSTR dir) +{ + char c, *d, *name; + BOOL ok = FALSE; + const char *s; + + TRACE("(fi == ^%p, lower == %s, dir == %s)\n", fi, lower ? "TRUE" : "FALSE", debugstr_a(dir)); + + if (!(name = malloc(strlen(fi->filename) + (dir ? strlen(dir) : 0) + 2))) { + ERR("out of memory!\n"); + return FALSE; + } + + /* start with blank name */ + *name = 0; + + /* add output directory if needed */ + if (dir) { + strcpy(name, dir); + strcat(name, "\\"); + } + + /* remove leading slashes */ + s = (char *) fi->filename; + while (*s == '\\') s++; + + /* copy from fi->filename to new name. + * lowercases characters if needed. + */ + d = &name[strlen(name)]; + do { + c = *s++; + *d++ = (lower ? tolower((unsigned char) c) : c); + } while (c); + + /* create directories if needed, attempt to write file */ + if (ensure_filepath(name)) { + fi->fh = CreateFileA(name, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (fi->fh != INVALID_HANDLE_VALUE) + ok = TRUE; + else { + ERR("CreateFileA returned INVALID_HANDLE_VALUE\n"); + fi->fh = 0; + } + } else + ERR("Couldn't ensure filepath for %s\n", debugstr_a(name)); + + if (!ok) { + ERR("Couldn't open file %s for writing\n", debugstr_a(name)); + } + + /* as full filename is no longer needed, free it */ + free(name); + + return ok; +} + +/******************************************************** + * close_file (internal) + * + * closes a completed file + */ +void file_close(struct cab_file *fi) +{ + TRACE("(fi == ^%p)\n", fi); + + if (fi->fh) { + CloseHandle(fi->fh); + } + fi->fh = 0; +} + +/****************************************************************** + * file_write (internal) + * + * writes from buf to a file specified as a cab_file struct. + * returns success/failure + */ +BOOL file_write(struct cab_file *fi, cab_UBYTE *buf, cab_off_t length) +{ + DWORD bytes_written; + + TRACE("(fi == ^%p, buf == ^%p, length == %u)\n", fi, buf, length); + + if ((!WriteFile( fi->fh, (LPCVOID) buf, length, &bytes_written, FALSE) || + (bytes_written != length))) { + ERR("Error writing file: %s\n", debugstr_a(fi->filename)); + return FALSE; + } + return TRUE; +} + + +/******************************************************************* + * cabinet_skip (internal) + * + * advance the file pointer associated with the cab structure + * by distance bytes + */ +void cabinet_skip(struct cabinet *cab, cab_off_t distance) +{ + TRACE("(cab == ^%p, distance == %u)\n", cab, distance); + if (SetFilePointer(cab->fh, distance, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER) { + if (distance != INVALID_SET_FILE_POINTER) + ERR("%s\n", debugstr_a(cab->filename)); + } +} + +/******************************************************************* + * cabinet_seek (internal) + * + * seek to the specified absolute offset in a cab + */ +void cabinet_seek(struct cabinet *cab, cab_off_t offset) { + TRACE("(cab == ^%p, offset == %u)\n", cab, offset); + if (SetFilePointer(cab->fh, offset, NULL, FILE_BEGIN) != offset) + ERR("%s seek failure\n", debugstr_a(cab->filename)); +} + +/******************************************************************* + * cabinet_getoffset (internal) + * + * returns the file pointer position of a cab + */ +cab_off_t cabinet_getoffset(struct cabinet *cab) +{ + return SetFilePointer(cab->fh, 0, NULL, FILE_CURRENT); +} + +/******************************************************************* + * cabinet_read (internal) + * + * read data from a cabinet, returns success + */ +BOOL cabinet_read(struct cabinet *cab, cab_UBYTE *buf, cab_off_t length) +{ + DWORD bytes_read; + cab_off_t avail = cab->filelen - cabinet_getoffset(cab); + + TRACE("(cab == ^%p, buf == ^%p, length == %u)\n", cab, buf, length); + + if (length > avail) { + WARN("%s: WARNING; cabinet is truncated\n", debugstr_a(cab->filename)); + length = avail; + } + + if (! ReadFile( cab->fh, (LPVOID) buf, length, &bytes_read, NULL )) { + ERR("%s read error\n", debugstr_a(cab->filename)); + return FALSE; + } else if (bytes_read != length) { + ERR("%s read size mismatch\n", debugstr_a(cab->filename)); + return FALSE; + } + + return TRUE; +} + +/********************************************************************** + * cabinet_read_string (internal) + * + * allocate and read an aribitrarily long string from the cabinet + */ +char *cabinet_read_string(struct cabinet *cab) +{ + cab_off_t len=256, base = cabinet_getoffset(cab), maxlen = cab->filelen - base; + BOOL ok = FALSE; + unsigned int i; + cab_UBYTE *buf = NULL; + + TRACE("(cab == ^%p)\n", cab); + + do { + if (len > maxlen) len = maxlen; + if (!(buf = realloc(buf, (size_t) len))) break; + if (!cabinet_read(cab, buf, (size_t) len)) break; + + /* search for a null terminator in what we've just read */ + for (i=0; i < len; i++) { + if (!buf[i]) {ok=TRUE; break;} + } + + if (!ok) { + if (len == maxlen) { + ERR("%s: WARNING; cabinet is truncated\n", debugstr_a(cab->filename)); + break; + } + len += 256; + cabinet_seek(cab, base); + } + } while (!ok); + + if (!ok) { + if (buf) + free(buf); + else + ERR("out of memory!\n"); + return NULL; + } + + /* otherwise, set the stream to just after the string and return */ + cabinet_seek(cab, base + ((cab_off_t) strlen((char *) buf)) + 1); + + return (char *) buf; +} + +/****************************************************************** + * cabinet_read_entries (internal) + * + * reads the header and all folder and file entries in this cabinet + */ +BOOL cabinet_read_entries(struct cabinet *cab) +{ + int num_folders, num_files, header_resv, folder_resv = 0, i; + struct cab_folder *fol, *linkfol = NULL; + struct cab_file *file, *linkfile = NULL; + cab_off_t base_offset; + cab_UBYTE buf[64]; + + TRACE("(cab == ^%p)\n", cab); + + /* read in the CFHEADER */ + base_offset = cabinet_getoffset(cab); + if (!cabinet_read(cab, buf, cfhead_SIZEOF)) { + return FALSE; + } + + /* check basic MSCF signature */ + if (EndGetI32(buf+cfhead_Signature) != 0x4643534d) { + ERR("%s: not a Microsoft cabinet file\n", debugstr_a(cab->filename)); + return FALSE; + } + + /* get the number of folders */ + num_folders = EndGetI16(buf+cfhead_NumFolders); + if (num_folders == 0) { + ERR("%s: no folders in cabinet\n", debugstr_a(cab->filename)); + return FALSE; + } + + /* get the number of files */ + num_files = EndGetI16(buf+cfhead_NumFiles); + if (num_files == 0) { + ERR("%s: no files in cabinet\n", debugstr_a(cab->filename)); + return FALSE; + } + + /* just check the header revision */ + if ((buf[cfhead_MajorVersion] > 1) || + (buf[cfhead_MajorVersion] == 1 && buf[cfhead_MinorVersion] > 3)) + { + WARN("%s: WARNING; cabinet format version > 1.3\n", debugstr_a(cab->filename)); + } + + /* read the reserved-sizes part of header, if present */ + cab->flags = EndGetI16(buf+cfhead_Flags); + if (cab->flags & cfheadRESERVE_PRESENT) { + if (!cabinet_read(cab, buf, cfheadext_SIZEOF)) return FALSE; + header_resv = EndGetI16(buf+cfheadext_HeaderReserved); + folder_resv = buf[cfheadext_FolderReserved]; + cab->block_resv = buf[cfheadext_DataReserved]; + + if (header_resv > 60000) { + WARN("%s: WARNING; header reserved space > 60000\n", debugstr_a(cab->filename)); + } + + /* skip the reserved header */ + if (header_resv) + if (SetFilePointer(cab->fh, (cab_off_t) header_resv, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER) + ERR("seek failure: %s\n", debugstr_a(cab->filename)); + } + + if (cab->flags & cfheadPREV_CABINET) { + cab->prevname = cabinet_read_string(cab); + if (!cab->prevname) return FALSE; + cab->previnfo = cabinet_read_string(cab); + } + + if (cab->flags & cfheadNEXT_CABINET) { + cab->nextname = cabinet_read_string(cab); + if (!cab->nextname) return FALSE; + cab->nextinfo = cabinet_read_string(cab); + } + + /* read folders */ + for (i = 0; i < num_folders; i++) { + if (!cabinet_read(cab, buf, cffold_SIZEOF)) return FALSE; + if (folder_resv) cabinet_skip(cab, folder_resv); + + fol = (struct cab_folder *) calloc(1, sizeof(struct cab_folder)); + if (!fol) { + ERR("out of memory!\n"); + return FALSE; + } + + fol->cab[0] = cab; + fol->offset[0] = base_offset + (cab_off_t) EndGetI32(buf+cffold_DataOffset); + fol->num_blocks = EndGetI16(buf+cffold_NumBlocks); + fol->comp_type = EndGetI16(buf+cffold_CompType); + + if (!linkfol) + cab->folders = fol; + else + linkfol->next = fol; + + linkfol = fol; + } + + /* read files */ + for (i = 0; i < num_files; i++) { + if (!cabinet_read(cab, buf, cffile_SIZEOF)) + return FALSE; + + file = (struct cab_file *) calloc(1, sizeof(struct cab_file)); + if (!file) { + ERR("out of memory!\n"); + return FALSE; + } + + file->length = EndGetI32(buf+cffile_UncompressedSize); + file->offset = EndGetI32(buf+cffile_FolderOffset); + file->index = EndGetI16(buf+cffile_FolderIndex); + file->time = EndGetI16(buf+cffile_Time); + file->date = EndGetI16(buf+cffile_Date); + file->attribs = EndGetI16(buf+cffile_Attribs); + file->filename = cabinet_read_string(cab); + + if (!file->filename) { + free(file); + return FALSE; + } + + if (!linkfile) + cab->files = file; + else + linkfile->next = file; + + linkfile = file; + } + return TRUE; +} + +/*********************************************************** + * load_cab_offset (internal) + * + * validates and reads file entries from a cabinet at offset [offset] in + * file [name]. Returns a cabinet structure if successful, or NULL + * otherwise. + */ +struct cabinet *load_cab_offset(LPCSTR name, cab_off_t offset) +{ + struct cabinet *cab = (struct cabinet *) calloc(1, sizeof(struct cabinet)); + int ok; + + TRACE("(name == %s, offset == %u)\n", debugstr_a(name), offset); + + if (!cab) return NULL; + + cab->filename = name; + if ((ok = cabinet_open(cab))) { + cabinet_seek(cab, offset); + ok = cabinet_read_entries(cab); + cabinet_close(cab); + } + + if (ok) return cab; + free(cab); + return NULL; +} + +/* MSZIP decruncher */ + +/* Dirk Stoecker wrote the ZIP decoder, based on the InfoZip deflate code */ + +/******************************************************** + * Ziphuft_free (internal) + */ +void Ziphuft_free(struct Ziphuft *t) +{ + register struct Ziphuft *p, *q; + + /* Go through linked list, freeing from the allocated (t[-1]) address. */ + p = t; + while (p != (struct Ziphuft *)NULL) + { + q = (--p)->v.t; + free(p); + p = q; + } +} + +/********************************************************* + * Ziphuft_build (internal) + */ +cab_LONG Ziphuft_build(cab_ULONG *b, cab_ULONG n, cab_ULONG s, cab_UWORD *d, cab_UWORD *e, +struct Ziphuft **t, cab_LONG *m, cab_decomp_state *decomp_state) +{ + cab_ULONG a; /* counter for codes of length k */ + cab_ULONG el; /* length of EOB code (value 256) */ + cab_ULONG f; /* i repeats in table every f entries */ + cab_LONG g; /* maximum code length */ + cab_LONG h; /* table level */ + register cab_ULONG i; /* counter, current code */ + register cab_ULONG j; /* counter */ + register cab_LONG k; /* number of bits in current code */ + cab_LONG *l; /* stack of bits per table */ + register cab_ULONG *p; /* pointer into ZIP(c)[],ZIP(b)[],ZIP(v)[] */ + register struct Ziphuft *q; /* points to current table */ + struct Ziphuft r; /* table entry for structure assignment */ + register cab_LONG w; /* bits before this table == (l * h) */ + cab_ULONG *xp; /* pointer into x */ + cab_LONG y; /* number of dummy codes added */ + cab_ULONG z; /* number of entries in current table */ + + l = ZIP(lx)+1; + + /* Generate counts for each bit length */ + el = n > 256 ? b[256] : ZIPBMAX; /* set length of EOB code, if any */ + + for(i = 0; i < ZIPBMAX+1; ++i) + ZIP(c)[i] = 0; + p = b; i = n; + do + { + ZIP(c)[*p]++; p++; /* assume all entries <= ZIPBMAX */ + } while (--i); + if (ZIP(c)[0] == n) /* null input--all zero length codes */ + { + *t = (struct Ziphuft *)NULL; + *m = 0; + return 0; + } + + /* Find minimum and maximum length, bound *m by those */ + for (j = 1; j <= ZIPBMAX; j++) + if (ZIP(c)[j]) + break; + k = j; /* minimum code length */ + if ((cab_ULONG)*m < j) + *m = j; + for (i = ZIPBMAX; i; i--) + if (ZIP(c)[i]) + break; + g = i; /* maximum code length */ + if ((cab_ULONG)*m > i) + *m = i; + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= ZIP(c)[j]) < 0) + return 2; /* bad input: more codes than bits */ + if ((y -= ZIP(c)[i]) < 0) + return 2; + ZIP(c)[i] += y; + + /* Generate starting offsets LONGo the value table for each length */ + ZIP(x)[1] = j = 0; + p = ZIP(c) + 1; xp = ZIP(x) + 2; + while (--i) + { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do{ + if ((j = *p++) != 0) + ZIP(v)[ZIP(x)[j]++] = i; + } while (++i < n); + + + /* Generate the Huffman codes and for each, make the table entries */ + ZIP(x)[0] = i = 0; /* first Huffman code is zero */ + p = ZIP(v); /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = l[-1] = 0; /* no bits decoded yet */ + ZIP(u)[0] = (struct Ziphuft *)NULL; /* just to keep compilers happy */ + q = (struct Ziphuft *)NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = ZIP(c)[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l[h]) + { + w += l[h++]; /* add bits already decoded */ + + /* compute minimum size table less than or equal to *m bits */ + z = (z = g - w) > (cab_ULONG)*m ? *m : z; /* upper limit */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = ZIP(c) + k; + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + if ((cab_ULONG)w + j > el && (cab_ULONG)w < el) + j = el - w; /* make EOB code end at table */ + z = 1 << j; /* table entries for j-bit table */ + l[h] = j; /* set table size in stack */ + + /* allocate and link in new table */ + if (!(q = (struct Ziphuft *) malloc((z + 1)*sizeof(struct Ziphuft)))) + { + if(h) + Ziphuft_free(ZIP(u)[0]); + return 3; /* not enough memory */ + } + *t = q + 1; /* link to list for Ziphuft_free() */ + *(t = &(q->v.t)) = (struct Ziphuft *)NULL; + ZIP(u)[h] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (h) + { + ZIP(x)[h] = i; /* save pattern for backing up */ + r.b = (cab_UBYTE)l[h-1]; /* bits to dump before this table */ + r.e = (cab_UBYTE)(16 + j); /* bits in this table */ + r.v.t = q; /* pointer to this table */ + j = (i & ((1 << w) - 1)) >> (w - l[h-1]); + ZIP(u)[h-1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.b = (cab_UBYTE)(k - w); + if (p >= ZIP(v) + n) + r.e = 99; /* out of values--invalid code */ + else if (*p < s) + { + r.e = (cab_UBYTE)(*p < 256 ? 16 : 15); /* 256 is end-of-block code */ + r.v.n = *p++; /* simple code is just the value */ + } + else + { + r.e = (cab_UBYTE)e[*p - s]; /* non-simple--look up in lists */ + r.v.n = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != ZIP(x)[h]) + w -= l[--h]; /* don't need to update q */ + } + } + + /* return actual size of base table */ + *m = l[0]; + + /* Return true (1) if we were given an incomplete table */ + return y != 0 && g != 1; +} + +/********************************************************* + * Zipinflate_codes (internal) + */ +cab_LONG Zipinflate_codes(struct Ziphuft *tl, struct Ziphuft *td, + cab_LONG bl, cab_LONG bd, cab_decomp_state *decomp_state) +{ + register cab_ULONG e; /* table entry flag/number of extra bits */ + cab_ULONG n, d; /* length and index for copy */ + cab_ULONG w; /* current window position */ + struct Ziphuft *t; /* pointer to table entry */ + cab_ULONG ml, md; /* masks for bl and bd bits */ + register cab_ULONG b; /* bit buffer */ + register cab_ULONG k; /* number of bits in bit buffer */ + + /* make local copies of globals */ + b = ZIP(bb); /* initialize bit buffer */ + k = ZIP(bk); + w = ZIP(window_posn); /* initialize window position */ + + /* inflate the coded data */ + ml = Zipmask[bl]; /* precompute masks for speed */ + md = Zipmask[bd]; + + for(;;) + { + ZIPNEEDBITS((cab_ULONG)bl) + if((e = (t = tl + ((cab_ULONG)b & ml))->e) > 16) + do + { + if (e == 99) + return 1; + ZIPDUMPBITS(t->b) + e -= 16; + ZIPNEEDBITS(e) + } while ((e = (t = t->v.t + ((cab_ULONG)b & Zipmask[e]))->e) > 16); + ZIPDUMPBITS(t->b) + if (e == 16) /* then it's a literal */ + CAB(outbuf)[w++] = (cab_UBYTE)t->v.n; + else /* it's an EOB or a length */ + { + /* exit if end of block */ + if(e == 15) + break; + + /* get length of block to copy */ + ZIPNEEDBITS(e) + n = t->v.n + ((cab_ULONG)b & Zipmask[e]); + ZIPDUMPBITS(e); + + /* decode distance of block to copy */ + ZIPNEEDBITS((cab_ULONG)bd) + if ((e = (t = td + ((cab_ULONG)b & md))->e) > 16) + do { + if (e == 99) + return 1; + ZIPDUMPBITS(t->b) + e -= 16; + ZIPNEEDBITS(e) + } while ((e = (t = t->v.t + ((cab_ULONG)b & Zipmask[e]))->e) > 16); + ZIPDUMPBITS(t->b) + ZIPNEEDBITS(e) + d = w - t->v.n - ((cab_ULONG)b & Zipmask[e]); + ZIPDUMPBITS(e) + do + { + n -= (e = (e = ZIPWSIZE - ((d &= ZIPWSIZE-1) > w ? d : w)) > n ?n:e); + do + { + CAB(outbuf)[w++] = CAB(outbuf)[d++]; + } while (--e); + } while (n); + } + } + + /* restore the globals from the locals */ + ZIP(window_posn) = w; /* restore global window pointer */ + ZIP(bb) = b; /* restore global bit buffer */ + ZIP(bk) = k; + + /* done */ + return 0; +} + +/*********************************************************** + * Zipinflate_stored (internal) + */ +cab_LONG Zipinflate_stored(cab_decomp_state *decomp_state) +/* "decompress" an inflated type 0 (stored) block. */ +{ + cab_ULONG n; /* number of bytes in block */ + cab_ULONG w; /* current window position */ + register cab_ULONG b; /* bit buffer */ + register cab_ULONG k; /* number of bits in bit buffer */ + + /* make local copies of globals */ + b = ZIP(bb); /* initialize bit buffer */ + k = ZIP(bk); + w = ZIP(window_posn); /* initialize window position */ + + /* go to byte boundary */ + n = k & 7; + ZIPDUMPBITS(n); + + /* get the length and its complement */ + ZIPNEEDBITS(16) + n = ((cab_ULONG)b & 0xffff); + ZIPDUMPBITS(16) + ZIPNEEDBITS(16) + if (n != (cab_ULONG)((~b) & 0xffff)) + return 1; /* error in compressed data */ + ZIPDUMPBITS(16) + + /* read and output the compressed data */ + while(n--) + { + ZIPNEEDBITS(8) + CAB(outbuf)[w++] = (cab_UBYTE)b; + ZIPDUMPBITS(8) + } + + /* restore the globals from the locals */ + ZIP(window_posn) = w; /* restore global window pointer */ + ZIP(bb) = b; /* restore global bit buffer */ + ZIP(bk) = k; + return 0; +} + +/****************************************************** + * Zipinflate_fixed (internal) + */ +cab_LONG Zipinflate_fixed(cab_decomp_state *decomp_state) +{ + struct Ziphuft *fixed_tl; + struct Ziphuft *fixed_td; + cab_LONG fixed_bl, fixed_bd; + cab_LONG i; /* temporary variable */ + cab_ULONG *l; + + l = ZIP(ll); + + /* literal table */ + for(i = 0; i < 144; i++) + l[i] = 8; + for(; i < 256; i++) + l[i] = 9; + for(; i < 280; i++) + l[i] = 7; + for(; i < 288; i++) /* make a complete, but wrong code set */ + l[i] = 8; + fixed_bl = 7; + if((i = Ziphuft_build(l, 288, 257, (cab_UWORD *) Zipcplens, + (cab_UWORD *) Zipcplext, &fixed_tl, &fixed_bl, decomp_state))) + return i; + + /* distance table */ + for(i = 0; i < 30; i++) /* make an incomplete code set */ + l[i] = 5; + fixed_bd = 5; + if((i = Ziphuft_build(l, 30, 0, (cab_UWORD *) Zipcpdist, (cab_UWORD *) Zipcpdext, + &fixed_td, &fixed_bd, decomp_state)) > 1) + { + Ziphuft_free(fixed_tl); + return i; + } + + /* decompress until an end-of-block code */ + i = Zipinflate_codes(fixed_tl, fixed_td, fixed_bl, fixed_bd, decomp_state); + + Ziphuft_free(fixed_td); + Ziphuft_free(fixed_tl); + return i; +} + +/************************************************************** + * Zipinflate_dynamic (internal) + */ +cab_LONG Zipinflate_dynamic(cab_decomp_state *decomp_state) + /* decompress an inflated type 2 (dynamic Huffman codes) block. */ +{ + cab_LONG i; /* temporary variables */ + cab_ULONG j; + cab_ULONG *ll; + cab_ULONG l; /* last length */ + cab_ULONG m; /* mask for bit lengths table */ + cab_ULONG n; /* number of lengths to get */ + struct Ziphuft *tl; /* literal/length code table */ + struct Ziphuft *td; /* distance code table */ + cab_LONG bl; /* lookup bits for tl */ + cab_LONG bd; /* lookup bits for td */ + cab_ULONG nb; /* number of bit length codes */ + cab_ULONG nl; /* number of literal/length codes */ + cab_ULONG nd; /* number of distance codes */ + register cab_ULONG b; /* bit buffer */ + register cab_ULONG k; /* number of bits in bit buffer */ + + /* make local bit buffer */ + b = ZIP(bb); + k = ZIP(bk); + ll = ZIP(ll); + + /* read in table lengths */ + ZIPNEEDBITS(5) + nl = 257 + ((cab_ULONG)b & 0x1f); /* number of literal/length codes */ + ZIPDUMPBITS(5) + ZIPNEEDBITS(5) + nd = 1 + ((cab_ULONG)b & 0x1f); /* number of distance codes */ + ZIPDUMPBITS(5) + ZIPNEEDBITS(4) + nb = 4 + ((cab_ULONG)b & 0xf); /* number of bit length codes */ + ZIPDUMPBITS(4) + if(nl > 288 || nd > 32) + return 1; /* bad lengths */ + + /* read in bit-length-code lengths */ + for(j = 0; j < nb; j++) + { + ZIPNEEDBITS(3) + ll[Zipborder[j]] = (cab_ULONG)b & 7; + ZIPDUMPBITS(3) + } + for(; j < 19; j++) + ll[Zipborder[j]] = 0; + + /* build decoding table for trees--single level, 7 bit lookup */ + bl = 7; + if((i = Ziphuft_build(ll, 19, 19, NULL, NULL, &tl, &bl, decomp_state)) != 0) + { + if(i == 1) + Ziphuft_free(tl); + return i; /* incomplete code set */ + } + + /* read in literal and distance code lengths */ + n = nl + nd; + m = Zipmask[bl]; + i = l = 0; + while((cab_ULONG)i < n) + { + ZIPNEEDBITS((cab_ULONG)bl) + j = (td = tl + ((cab_ULONG)b & m))->b; + ZIPDUMPBITS(j) + j = td->v.n; + if (j < 16) /* length of code in bits (0..15) */ + ll[i++] = l = j; /* save last length in l */ + else if (j == 16) /* repeat last length 3 to 6 times */ + { + ZIPNEEDBITS(2) + j = 3 + ((cab_ULONG)b & 3); + ZIPDUMPBITS(2) + if((cab_ULONG)i + j > n) + return 1; + while (j--) + ll[i++] = l; + } + else if (j == 17) /* 3 to 10 zero length codes */ + { + ZIPNEEDBITS(3) + j = 3 + ((cab_ULONG)b & 7); + ZIPDUMPBITS(3) + if ((cab_ULONG)i + j > n) + return 1; + while (j--) + ll[i++] = 0; + l = 0; + } + else /* j == 18: 11 to 138 zero length codes */ + { + ZIPNEEDBITS(7) + j = 11 + ((cab_ULONG)b & 0x7f); + ZIPDUMPBITS(7) + if ((cab_ULONG)i + j > n) + return 1; + while (j--) + ll[i++] = 0; + l = 0; + } + } + + /* free decoding table for trees */ + Ziphuft_free(tl); + + /* restore the global bit buffer */ + ZIP(bb) = b; + ZIP(bk) = k; + + /* build the decoding tables for literal/length and distance codes */ + bl = ZIPLBITS; + if((i = Ziphuft_build(ll, nl, 257, (cab_UWORD *) Zipcplens, (cab_UWORD *) Zipcplext, + &tl, &bl, decomp_state)) != 0) + { + if(i == 1) + Ziphuft_free(tl); + return i; /* incomplete code set */ + } + bd = ZIPDBITS; + Ziphuft_build(ll + nl, nd, 0, (cab_UWORD *) Zipcpdist, (cab_UWORD *) Zipcpdext, + &td, &bd, decomp_state); + + /* decompress until an end-of-block code */ + if(Zipinflate_codes(tl, td, bl, bd, decomp_state)) + return 1; + + /* free the decoding tables, return */ + Ziphuft_free(tl); + Ziphuft_free(td); + return 0; +} + +/***************************************************** + * Zipinflate_block (internal) + */ +cab_LONG Zipinflate_block(cab_LONG *e, cab_decomp_state *decomp_state) /* e == last block flag */ +{ /* decompress an inflated block */ + cab_ULONG t; /* block type */ + register cab_ULONG b; /* bit buffer */ + register cab_ULONG k; /* number of bits in bit buffer */ + + /* make local bit buffer */ + b = ZIP(bb); + k = ZIP(bk); + + /* read in last block bit */ + ZIPNEEDBITS(1) + *e = (cab_LONG)b & 1; + ZIPDUMPBITS(1) + + /* read in block type */ + ZIPNEEDBITS(2) + t = (cab_ULONG)b & 3; + ZIPDUMPBITS(2) + + /* restore the global bit buffer */ + ZIP(bb) = b; + ZIP(bk) = k; + + /* inflate that block type */ + if(t == 2) + return Zipinflate_dynamic(decomp_state); + if(t == 0) + return Zipinflate_stored(decomp_state); + if(t == 1) + return Zipinflate_fixed(decomp_state); + /* bad block type */ + return 2; +} + +/**************************************************** + * ZIPdecompress (internal) + */ +int ZIPdecompress(int inlen, int outlen, cab_decomp_state *decomp_state) +{ + cab_LONG e; /* last block flag */ + + TRACE("(inlen == %d, outlen == %d)\n", inlen, outlen); + + ZIP(inpos) = CAB(inbuf); + ZIP(bb) = ZIP(bk) = ZIP(window_posn) = 0; + if(outlen > ZIPWSIZE) + return DECR_DATAFORMAT; + + /* CK = Chris Kirmse, official Microsoft purloiner */ + if(ZIP(inpos)[0] != 0x43 || ZIP(inpos)[1] != 0x4B) + return DECR_ILLEGALDATA; + ZIP(inpos) += 2; + + do + { + if(Zipinflate_block(&e, decomp_state)) + return DECR_ILLEGALDATA; + } while(!e); + + /* return success */ + return DECR_OK; +} + +/* Quantum decruncher */ + +/* This decruncher was researched and implemented by Matthew Russoto. */ +/* It has since been tidied up by Stuart Caie */ + +/****************************************************************** + * QTMinitmodel (internal) + * + * Initialise a model which decodes symbols from [s] to [s]+[n]-1 + */ +void QTMinitmodel(struct QTMmodel *m, struct QTMmodelsym *sym, int n, int s) { + int i; + m->shiftsleft = 4; + m->entries = n; + m->syms = sym; + memset(m->tabloc, 0xFF, sizeof(m->tabloc)); /* clear out look-up table */ + for (i = 0; i < n; i++) { + m->tabloc[i+s] = i; /* set up a look-up entry for symbol */ + m->syms[i].sym = i+s; /* actual symbol */ + m->syms[i].cumfreq = n-i; /* current frequency of that symbol */ + } + m->syms[n].cumfreq = 0; +} + +/****************************************************************** + * QTMinit (internal) + */ +int QTMinit(int window, int level, cab_decomp_state *decomp_state) { + unsigned int wndsize = 1 << window; + int msz = window * 2, i; + cab_ULONG j; + + /* QTM supports window sizes of 2^10 (1Kb) through 2^21 (2Mb) */ + /* if a previously allocated window is big enough, keep it */ + if (window < 10 || window > 21) return DECR_DATAFORMAT; + if (QTM(actual_size) < wndsize) { + if (QTM(window)) free(QTM(window)); + QTM(window) = NULL; + } + if (!QTM(window)) { + if (!(QTM(window) = malloc(wndsize))) return DECR_NOMEMORY; + QTM(actual_size) = wndsize; + } + QTM(window_size) = wndsize; + QTM(window_posn) = 0; + + /* initialise static slot/extrabits tables */ + for (i = 0, j = 0; i < 27; i++) { + CAB(q_length_extra)[i] = (i == 26) ? 0 : (i < 2 ? 0 : i - 2) >> 2; + CAB(q_length_base)[i] = j; j += 1 << ((i == 26) ? 5 : CAB(q_length_extra)[i]); + } + for (i = 0, j = 0; i < 42; i++) { + CAB(q_extra_bits)[i] = (i < 2 ? 0 : i-2) >> 1; + CAB(q_position_base)[i] = j; j += 1 << CAB(q_extra_bits)[i]; + } + + /* initialise arithmetic coding models */ + + QTMinitmodel(&QTM(model7), &QTM(m7sym)[0], 7, 0); + + QTMinitmodel(&QTM(model00), &QTM(m00sym)[0], 0x40, 0x00); + QTMinitmodel(&QTM(model40), &QTM(m40sym)[0], 0x40, 0x40); + QTMinitmodel(&QTM(model80), &QTM(m80sym)[0], 0x40, 0x80); + QTMinitmodel(&QTM(modelC0), &QTM(mC0sym)[0], 0x40, 0xC0); + + /* model 4 depends on table size, ranges from 20 to 24 */ + QTMinitmodel(&QTM(model4), &QTM(m4sym)[0], (msz < 24) ? msz : 24, 0); + /* model 5 depends on table size, ranges from 20 to 36 */ + QTMinitmodel(&QTM(model5), &QTM(m5sym)[0], (msz < 36) ? msz : 36, 0); + /* model 6pos depends on table size, ranges from 20 to 42 */ + QTMinitmodel(&QTM(model6pos), &QTM(m6psym)[0], msz, 0); + QTMinitmodel(&QTM(model6len), &QTM(m6lsym)[0], 27, 0); + + return DECR_OK; +} + +/**************************************************************** + * QTMupdatemodel (internal) + */ +void QTMupdatemodel(struct QTMmodel *model, int sym) { + struct QTMmodelsym temp; + int i, j; + + for (i = 0; i < sym; i++) model->syms[i].cumfreq += 8; + + if (model->syms[0].cumfreq > 3800) { + if (--model->shiftsleft) { + for (i = model->entries - 1; i >= 0; i--) { + /* -1, not -2; the 0 entry saves this */ + model->syms[i].cumfreq >>= 1; + if (model->syms[i].cumfreq <= model->syms[i+1].cumfreq) { + model->syms[i].cumfreq = model->syms[i+1].cumfreq + 1; + } + } + } + else { + model->shiftsleft = 50; + for (i = 0; i < model->entries ; i++) { + /* no -1, want to include the 0 entry */ + /* this converts cumfreqs into frequencies, then shifts right */ + model->syms[i].cumfreq -= model->syms[i+1].cumfreq; + model->syms[i].cumfreq++; /* avoid losing things entirely */ + model->syms[i].cumfreq >>= 1; + } + + /* now sort by frequencies, decreasing order -- this must be an + * inplace selection sort, or a sort with the same (in)stability + * characteristics + */ + for (i = 0; i < model->entries - 1; i++) { + for (j = i + 1; j < model->entries; j++) { + if (model->syms[i].cumfreq < model->syms[j].cumfreq) { + temp = model->syms[i]; + model->syms[i] = model->syms[j]; + model->syms[j] = temp; + } + } + } + + /* then convert frequencies back to cumfreq */ + for (i = model->entries - 1; i >= 0; i--) { + model->syms[i].cumfreq += model->syms[i+1].cumfreq; + } + /* then update the other part of the table */ + for (i = 0; i < model->entries; i++) { + model->tabloc[model->syms[i].sym] = i; + } + } + } +} + +/******************************************************************* + * QTMdecompress (internal) + */ +int QTMdecompress(int inlen, int outlen, cab_decomp_state *decomp_state) +{ + cab_UBYTE *inpos = CAB(inbuf); + cab_UBYTE *window = QTM(window); + cab_UBYTE *runsrc, *rundest; + + cab_ULONG window_posn = QTM(window_posn); + cab_ULONG window_size = QTM(window_size); + + /* used by bitstream macros */ + register int bitsleft, bitrun, bitsneed; + register cab_ULONG bitbuf; + + /* used by GET_SYMBOL */ + cab_ULONG range; + cab_UWORD symf; + int i; + + int extra, togo = outlen, match_length = 0, copy_length; + cab_UBYTE selector, sym; + cab_ULONG match_offset = 0; + + cab_UWORD H = 0xFFFF, L = 0, C; + + TRACE("(inlen == %d, outlen == %d)\n", inlen, outlen); + + /* read initial value of C */ + Q_INIT_BITSTREAM; + Q_READ_BITS(C, 16); + + /* apply 2^x-1 mask */ + window_posn &= window_size - 1; + /* runs can't straddle the window wraparound */ + if ((window_posn + togo) > window_size) { + TRACE("straddled run\n"); + return DECR_DATAFORMAT; + } + + while (togo > 0) { + GET_SYMBOL(model7, selector); + switch (selector) { + case 0: + GET_SYMBOL(model00, sym); window[window_posn++] = sym; togo--; + break; + case 1: + GET_SYMBOL(model40, sym); window[window_posn++] = sym; togo--; + break; + case 2: + GET_SYMBOL(model80, sym); window[window_posn++] = sym; togo--; + break; + case 3: + GET_SYMBOL(modelC0, sym); window[window_posn++] = sym; togo--; + break; + + case 4: + /* selector 4 = fixed length of 3 */ + GET_SYMBOL(model4, sym); + Q_READ_BITS(extra, CAB(q_extra_bits)[sym]); + match_offset = CAB(q_position_base)[sym] + extra + 1; + match_length = 3; + break; + + case 5: + /* selector 5 = fixed length of 4 */ + GET_SYMBOL(model5, sym); + Q_READ_BITS(extra, CAB(q_extra_bits)[sym]); + match_offset = CAB(q_position_base)[sym] + extra + 1; + match_length = 4; + break; + + case 6: + /* selector 6 = variable length */ + GET_SYMBOL(model6len, sym); + Q_READ_BITS(extra, CAB(q_length_extra)[sym]); + match_length = CAB(q_length_base)[sym] + extra + 5; + GET_SYMBOL(model6pos, sym); + Q_READ_BITS(extra, CAB(q_extra_bits)[sym]); + match_offset = CAB(q_position_base)[sym] + extra + 1; + break; + + default: + TRACE("Selector is bogus\n"); + return DECR_ILLEGALDATA; + } + + /* if this is a match */ + if (selector >= 4) { + rundest = window + window_posn; + togo -= match_length; + + /* copy any wrapped around source data */ + if (window_posn >= match_offset) { + /* no wrap */ + runsrc = rundest - match_offset; + } else { + runsrc = rundest + (window_size - match_offset); + copy_length = match_offset - window_posn; + if (copy_length < match_length) { + match_length -= copy_length; + window_posn += copy_length; + while (copy_length-- > 0) *rundest++ = *runsrc++; + runsrc = window; + } + } + window_posn += match_length; + + /* copy match data - no worries about destination wraps */ + while (match_length-- > 0) *rundest++ = *runsrc++; + } + } /* while (togo > 0) */ + + if (togo != 0) { + TRACE("Frame overflow, this_run = %d\n", togo); + return DECR_ILLEGALDATA; + } + + memcpy(CAB(outbuf), window + ((!window_posn) ? window_size : window_posn) - + outlen, outlen); + + QTM(window_posn) = window_posn; + return DECR_OK; +} + +/* LZX decruncher */ + +/* Microsoft's LZX document and their implementation of the + * com.ms.util.cab Java package do not concur. + * + * In the LZX document, there is a table showing the correlation between + * window size and the number of position slots. It states that the 1MB + * window = 40 slots and the 2MB window = 42 slots. In the implementation, + * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the + * first slot whose position base is equal to or more than the required + * window size'. This would explain why other tables in the document refer + * to 50 slots rather than 42. + * + * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode + * is not defined in the specification. + * + * The LZX document does not state the uncompressed block has an + * uncompressed length field. Where does this length field come from, so + * we can know how large the block is? The implementation has it as the 24 + * bits following after the 3 blocktype bits, before the alignment + * padding. + * + * The LZX document states that aligned offset blocks have their aligned + * offset huffman tree AFTER the main and length trees. The implementation + * suggests that the aligned offset tree is BEFORE the main and length + * trees. + * + * The LZX document decoding algorithm states that, in an aligned offset + * block, if an extra_bits value is 1, 2 or 3, then that number of bits + * should be read and the result added to the match offset. This is + * correct for 1 and 2, but not 3, where just a huffman symbol (using the + * aligned tree) should be read. + * + * Regarding the E8 preprocessing, the LZX document states 'No translation + * may be performed on the last 6 bytes of the input block'. This is + * correct. However, the pseudocode provided checks for the *E8 leader* + * up to the last 6 bytes. If the leader appears between -10 and -7 bytes + * from the end, this would cause the next four bytes to be modified, at + * least one of which would be in the last 6 bytes, which is not allowed + * according to the spec. + * + * The specification states that the huffman trees must always contain at + * least one element. However, many CAB files contain blocks where the + * length tree is completely empty (because there are no matches), and + * this is expected to succeed. + */ + + +/* LZX uses what it calls 'position slots' to represent match offsets. + * What this means is that a small 'position slot' number and a small + * offset from that slot are encoded instead of one large offset for + * every match. + * - lzx_position_base is an index to the position slot bases + * - lzx_extra_bits states how many bits of offset-from-base data is needed. + */ + +/************************************************************ + * LZXinit (internal) + */ +int LZXinit(int window, cab_decomp_state *decomp_state) { + cab_ULONG wndsize = 1 << window; + int i, j, posn_slots; + + /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */ + /* if a previously allocated window is big enough, keep it */ + if (window < 15 || window > 21) return DECR_DATAFORMAT; + if (LZX(actual_size) < wndsize) { + if (LZX(window)) free(LZX(window)); + LZX(window) = NULL; + } + if (!LZX(window)) { + if (!(LZX(window) = malloc(wndsize))) return DECR_NOMEMORY; + LZX(actual_size) = wndsize; + } + LZX(window_size) = wndsize; + + /* initialise static tables */ + for (i=0, j=0; i <= 50; i += 2) { + CAB(extra_bits)[i] = CAB(extra_bits)[i+1] = j; /* 0,0,0,0,1,1,2,2,3,3... */ + if ((i != 0) && (j < 17)) j++; /* 0,0,1,2,3,4...15,16,17,17,17,17... */ + } + for (i=0, j=0; i <= 50; i++) { + CAB(lzx_position_base)[i] = j; /* 0,1,2,3,4,6,8,12,16,24,32,... */ + j += 1 << CAB(extra_bits)[i]; /* 1,1,1,1,2,2,4,4,8,8,16,16,32,32,... */ + } + + /* calculate required position slots */ + if (window == 20) posn_slots = 42; + else if (window == 21) posn_slots = 50; + else posn_slots = window << 1; + + /*posn_slots=i=0; while (i < wndsize) i += 1 << CAB(extra_bits)[posn_slots++]; */ + + LZX(R0) = LZX(R1) = LZX(R2) = 1; + LZX(main_elements) = LZX_NUM_CHARS + (posn_slots << 3); + LZX(header_read) = 0; + LZX(frames_read) = 0; + LZX(block_remaining) = 0; + LZX(block_type) = LZX_BLOCKTYPE_INVALID; + LZX(intel_curpos) = 0; + LZX(intel_started) = 0; + LZX(window_posn) = 0; + + /* initialise tables to 0 (because deltas will be applied to them) */ + for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) LZX(MAINTREE_len)[i] = 0; + for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) LZX(LENGTH_len)[i] = 0; + + return DECR_OK; +} + +/************************************************************************* + * make_decode_table (internal) + * + * This function was coded by David Tritscher. It builds a fast huffman + * decoding table out of just a canonical huffman code lengths table. + * + * PARAMS + * nsyms: total number of symbols in this huffman tree. + * nbits: any symbols with a code length of nbits or less can be decoded + * in one lookup of the table. + * length: A table to get code lengths from [0 to syms-1] + * table: The table to fill up with decoded symbols and pointers. + * + * RETURNS + * OK: 0 + * error: 1 + */ +int make_decode_table(cab_ULONG nsyms, cab_ULONG nbits, cab_UBYTE *length, cab_UWORD *table) { + register cab_UWORD sym; + register cab_ULONG leaf; + register cab_UBYTE bit_num = 1; + cab_ULONG fill; + cab_ULONG pos = 0; /* the current position in the decode table */ + cab_ULONG table_mask = 1 << nbits; + cab_ULONG bit_mask = table_mask >> 1; /* don't do 0 length codes */ + cab_ULONG next_symbol = bit_mask; /* base of allocation for long codes */ + + /* fill entries for codes short enough for a direct mapping */ + while (bit_num <= nbits) { + for (sym = 0; sym < nsyms; sym++) { + if (length[sym] == bit_num) { + leaf = pos; + + if((pos += bit_mask) > table_mask) return 1; /* table overrun */ + + /* fill all possible lookups of this symbol with the symbol itself */ + fill = bit_mask; + while (fill-- > 0) table[leaf++] = sym; + } + } + bit_mask >>= 1; + bit_num++; + } + + /* if there are any codes longer than nbits */ + if (pos != table_mask) { + /* clear the remainder of the table */ + for (sym = pos; sym < table_mask; sym++) table[sym] = 0; + + /* give ourselves room for codes to grow by up to 16 more bits */ + pos <<= 16; + table_mask <<= 16; + bit_mask = 1 << 15; + + while (bit_num <= 16) { + for (sym = 0; sym < nsyms; sym++) { + if (length[sym] == bit_num) { + leaf = pos >> 16; + for (fill = 0; fill < bit_num - nbits; fill++) { + /* if this path hasn't been taken yet, 'allocate' two entries */ + if (table[leaf] == 0) { + table[(next_symbol << 1)] = 0; + table[(next_symbol << 1) + 1] = 0; + table[leaf] = next_symbol++; + } + /* follow the path and select either left or right for next bit */ + leaf = table[leaf] << 1; + if ((pos >> (15-fill)) & 1) leaf++; + } + table[leaf] = sym; + + if ((pos += bit_mask) > table_mask) return 1; /* table overflow */ + } + } + bit_mask >>= 1; + bit_num++; + } + } + + /* full table? */ + if (pos == table_mask) return 0; + + /* either erroneous table, or all elements are 0 - let's find out. */ + for (sym = 0; sym < nsyms; sym++) if (length[sym]) return 1; + return 0; +} + +/************************************************************ + * lzx_read_lens (internal) + */ +int lzx_read_lens(cab_UBYTE *lens, cab_ULONG first, cab_ULONG last, struct lzx_bits *lb, + cab_decomp_state *decomp_state) { + cab_ULONG i,j, x,y; + int z; + + register cab_ULONG bitbuf = lb->bb; + register int bitsleft = lb->bl; + cab_UBYTE *inpos = lb->ip; + cab_UWORD *hufftbl; + + for (x = 0; x < 20; x++) { + READ_BITS(y, 4); + LENTABLE(PRETREE)[x] = y; + } + BUILD_TABLE(PRETREE); + + for (x = first; x < last; ) { + READ_HUFFSYM(PRETREE, z); + if (z == 17) { + READ_BITS(y, 4); y += 4; + while (y--) lens[x++] = 0; + } + else if (z == 18) { + READ_BITS(y, 5); y += 20; + while (y--) lens[x++] = 0; + } + else if (z == 19) { + READ_BITS(y, 1); y += 4; + READ_HUFFSYM(PRETREE, z); + z = lens[x] - z; if (z < 0) z += 17; + while (y--) lens[x++] = z; + } + else { + z = lens[x] - z; if (z < 0) z += 17; + lens[x++] = z; + } + } + + lb->bb = bitbuf; + lb->bl = bitsleft; + lb->ip = inpos; + return 0; +} + +/******************************************************* + * LZXdecompress (internal) + */ +int LZXdecompress(int inlen, int outlen, cab_decomp_state *decomp_state) { + cab_UBYTE *inpos = CAB(inbuf); + cab_UBYTE *endinp = inpos + inlen; + cab_UBYTE *window = LZX(window); + cab_UBYTE *runsrc, *rundest; + cab_UWORD *hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */ + + cab_ULONG window_posn = LZX(window_posn); + cab_ULONG window_size = LZX(window_size); + cab_ULONG R0 = LZX(R0); + cab_ULONG R1 = LZX(R1); + cab_ULONG R2 = LZX(R2); + + register cab_ULONG bitbuf; + register int bitsleft; + cab_ULONG match_offset, i,j,k; /* ijk used in READ_HUFFSYM macro */ + struct lzx_bits lb; /* used in READ_LENGTHS macro */ + + int togo = outlen, this_run, main_element, aligned_bits; + int match_length, copy_length, length_footer, extra, verbatim_bits; + + TRACE("(inlen == %d, outlen == %d)\n", inlen, outlen); + + INIT_BITSTREAM; + + /* read header if necessary */ + if (!LZX(header_read)) { + i = j = 0; + READ_BITS(k, 1); if (k) { READ_BITS(i,16); READ_BITS(j,16); } + LZX(intel_filesize) = (i << 16) | j; /* or 0 if not encoded */ + LZX(header_read) = 1; + } + + /* main decoding loop */ + while (togo > 0) { + /* last block finished, new block expected */ + if (LZX(block_remaining) == 0) { + if (LZX(block_type) == LZX_BLOCKTYPE_UNCOMPRESSED) { + if (LZX(block_length) & 1) inpos++; /* realign bitstream to word */ + INIT_BITSTREAM; + } + + READ_BITS(LZX(block_type), 3); + READ_BITS(i, 16); + READ_BITS(j, 8); + LZX(block_remaining) = LZX(block_length) = (i << 8) | j; + + switch (LZX(block_type)) { + case LZX_BLOCKTYPE_ALIGNED: + for (i = 0; i < 8; i++) { READ_BITS(j, 3); LENTABLE(ALIGNED)[i] = j; } + BUILD_TABLE(ALIGNED); + /* rest of aligned header is same as verbatim */ + + case LZX_BLOCKTYPE_VERBATIM: + READ_LENGTHS(MAINTREE, 0, 256, lzx_read_lens); + READ_LENGTHS(MAINTREE, 256, LZX(main_elements), lzx_read_lens); + BUILD_TABLE(MAINTREE); + if (LENTABLE(MAINTREE)[0xE8] != 0) LZX(intel_started) = 1; + + READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS, lzx_read_lens); + BUILD_TABLE(LENGTH); + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + LZX(intel_started) = 1; /* because we can't assume otherwise */ + ENSURE_BITS(16); /* get up to 16 pad bits into the buffer */ + if (bitsleft > 16) inpos -= 2; /* and align the bitstream! */ + R0 = inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4; + R1 = inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4; + R2 = inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4; + break; + + default: + return DECR_ILLEGALDATA; + } + } + + /* buffer exhaustion check */ + if (inpos > endinp) { + /* it's possible to have a file where the next run is less than + * 16 bits in size. In this case, the READ_HUFFSYM() macro used + * in building the tables will exhaust the buffer, so we should + * allow for this, but not allow those accidentally read bits to + * be used (so we check that there are at least 16 bits + * remaining - in this boundary case they aren't really part of + * the compressed data) + */ + if (inpos > (endinp+2) || bitsleft < 16) return DECR_ILLEGALDATA; + } + + while ((this_run = LZX(block_remaining)) > 0 && togo > 0) { + if (this_run > togo) this_run = togo; + togo -= this_run; + LZX(block_remaining) -= this_run; + + /* apply 2^x-1 mask */ + window_posn &= window_size - 1; + /* runs can't straddle the window wraparound */ + if ((window_posn + this_run) > window_size) + return DECR_DATAFORMAT; + + switch (LZX(block_type)) { + + case LZX_BLOCKTYPE_VERBATIM: + while (this_run > 0) { + READ_HUFFSYM(MAINTREE, main_element); + + if (main_element < LZX_NUM_CHARS) { + /* literal: 0 to LZX_NUM_CHARS-1 */ + window[window_posn++] = main_element; + this_run--; + } + else { + /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ + main_element -= LZX_NUM_CHARS; + + match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) { + READ_HUFFSYM(LENGTH, length_footer); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + match_offset = main_element >> 3; + + if (match_offset > 2) { + /* not repeated offset */ + if (match_offset != 3) { + extra = CAB(extra_bits)[match_offset]; + READ_BITS(verbatim_bits, extra); + match_offset = CAB(lzx_position_base)[match_offset] + - 2 + verbatim_bits; + } + else { + match_offset = 1; + } + + /* update repeated offset LRU queue */ + R2 = R1; R1 = R0; R0 = match_offset; + } + else if (match_offset == 0) { + match_offset = R0; + } + else if (match_offset == 1) { + match_offset = R1; + R1 = R0; R0 = match_offset; + } + else /* match_offset == 2 */ { + match_offset = R2; + R2 = R0; R0 = match_offset; + } + + rundest = window + window_posn; + this_run -= match_length; + + /* copy any wrapped around source data */ + if (window_posn >= match_offset) { + /* no wrap */ + runsrc = rundest - match_offset; + } else { + runsrc = rundest + (window_size - match_offset); + copy_length = match_offset - window_posn; + if (copy_length < match_length) { + match_length -= copy_length; + window_posn += copy_length; + while (copy_length-- > 0) *rundest++ = *runsrc++; + runsrc = window; + } + } + window_posn += match_length; + + /* copy match data - no worries about destination wraps */ + while (match_length-- > 0) *rundest++ = *runsrc++; + } + } + break; + + case LZX_BLOCKTYPE_ALIGNED: + while (this_run > 0) { + READ_HUFFSYM(MAINTREE, main_element); + + if (main_element < LZX_NUM_CHARS) { + /* literal: 0 to LZX_NUM_CHARS-1 */ + window[window_posn++] = main_element; + this_run--; + } + else { + /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ + main_element -= LZX_NUM_CHARS; + + match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) { + READ_HUFFSYM(LENGTH, length_footer); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + match_offset = main_element >> 3; + + if (match_offset > 2) { + /* not repeated offset */ + extra = CAB(extra_bits)[match_offset]; + match_offset = CAB(lzx_position_base)[match_offset] - 2; + if (extra > 3) { + /* verbatim and aligned bits */ + extra -= 3; + READ_BITS(verbatim_bits, extra); + match_offset += (verbatim_bits << 3); + READ_HUFFSYM(ALIGNED, aligned_bits); + match_offset += aligned_bits; + } + else if (extra == 3) { + /* aligned bits only */ + READ_HUFFSYM(ALIGNED, aligned_bits); + match_offset += aligned_bits; + } + else if (extra > 0) { /* extra==1, extra==2 */ + /* verbatim bits only */ + READ_BITS(verbatim_bits, extra); + match_offset += verbatim_bits; + } + else /* extra == 0 */ { + /* ??? */ + match_offset = 1; + } + + /* update repeated offset LRU queue */ + R2 = R1; R1 = R0; R0 = match_offset; + } + else if (match_offset == 0) { + match_offset = R0; + } + else if (match_offset == 1) { + match_offset = R1; + R1 = R0; R0 = match_offset; + } + else /* match_offset == 2 */ { + match_offset = R2; + R2 = R0; R0 = match_offset; + } + + rundest = window + window_posn; + this_run -= match_length; + + /* copy any wrapped around source data */ + if (window_posn >= match_offset) { + /* no wrap */ + runsrc = rundest - match_offset; + } else { + runsrc = rundest + (window_size - match_offset); + copy_length = match_offset - window_posn; + if (copy_length < match_length) { + match_length -= copy_length; + window_posn += copy_length; + while (copy_length-- > 0) *rundest++ = *runsrc++; + runsrc = window; + } + } + window_posn += match_length; + + /* copy match data - no worries about destination wraps */ + while (match_length-- > 0) *rundest++ = *runsrc++; + } + } + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + if ((inpos + this_run) > endinp) return DECR_ILLEGALDATA; + memcpy(window + window_posn, inpos, (size_t) this_run); + inpos += this_run; window_posn += this_run; + break; + + default: + return DECR_ILLEGALDATA; /* might as well */ + } + + } + } + + if (togo != 0) return DECR_ILLEGALDATA; + memcpy(CAB(outbuf), window + ((!window_posn) ? window_size : window_posn) - + outlen, (size_t) outlen); + + LZX(window_posn) = window_posn; + LZX(R0) = R0; + LZX(R1) = R1; + LZX(R2) = R2; + + /* intel E8 decoding */ + if ((LZX(frames_read)++ < 32768) && LZX(intel_filesize) != 0) { + if (outlen <= 6 || !LZX(intel_started)) { + LZX(intel_curpos) += outlen; + } + else { + cab_UBYTE *data = CAB(outbuf); + cab_UBYTE *dataend = data + outlen - 10; + cab_LONG curpos = LZX(intel_curpos); + cab_LONG filesize = LZX(intel_filesize); + cab_LONG abs_off, rel_off; + + LZX(intel_curpos) = curpos + outlen; + + while (data < dataend) { + if (*data++ != 0xE8) { curpos++; continue; } + abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); + if ((abs_off >= -curpos) && (abs_off < filesize)) { + rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; + data[0] = (cab_UBYTE) rel_off; + data[1] = (cab_UBYTE) (rel_off >> 8); + data[2] = (cab_UBYTE) (rel_off >> 16); + data[3] = (cab_UBYTE) (rel_off >> 24); + } + data += 4; + curpos += 5; + } + } + } + return DECR_OK; +} + +/********************************************************* + * find_cabs_in_file (internal) + */ +struct cabinet *find_cabs_in_file(LPCSTR name, cab_UBYTE search_buf[]) +{ + struct cabinet *cab, *cab2, *firstcab = NULL, *linkcab = NULL; + cab_UBYTE *pstart = &search_buf[0], *pend, *p; + cab_off_t offset, caboff, cablen = 0, foffset = 0, filelen, length; + int state = 0, found = 0, ok = 0; + + TRACE("(name == %s)\n", debugstr_a(name)); + + /* open the file and search for cabinet headers */ + if ((cab = (struct cabinet *) calloc(1, sizeof(struct cabinet)))) { + cab->filename = name; + if (cabinet_open(cab)) { + filelen = cab->filelen; + for (offset = 0; (offset < filelen); offset += length) { + /* search length is either the full length of the search buffer, + * or the amount of data remaining to the end of the file, + * whichever is less. + */ + length = filelen - offset; + if (length > CAB_SEARCH_SIZE) length = CAB_SEARCH_SIZE; + + /* fill the search buffer with data from disk */ + if (!cabinet_read(cab, search_buf, length)) break; + + /* read through the entire buffer. */ + p = pstart; + pend = &search_buf[length]; + while (p < pend) { + switch (state) { + /* starting state */ + case 0: + /* we spend most of our time in this while loop, looking for + * a leading 'M' of the 'MSCF' signature + */ + while (*p++ != 0x4D && p < pend); + if (p < pend) state = 1; /* if we found tht 'M', advance state */ + break; + + /* verify that the next 3 bytes are 'S', 'C' and 'F' */ + case 1: state = (*p++ == 0x53) ? 2 : 0; break; + case 2: state = (*p++ == 0x43) ? 3 : 0; break; + case 3: state = (*p++ == 0x46) ? 4 : 0; break; + + /* we don't care about bytes 4-7 */ + /* bytes 8-11 are the overall length of the cabinet */ + case 8: cablen = *p++; state++; break; + case 9: cablen |= *p++ << 8; state++; break; + case 10: cablen |= *p++ << 16; state++; break; + case 11: cablen |= *p++ << 24; state++; break; + + /* we don't care about bytes 12-15 */ + /* bytes 16-19 are the offset within the cabinet of the filedata */ + case 16: foffset = *p++; state++; break; + case 17: foffset |= *p++ << 8; state++; break; + case 18: foffset |= *p++ << 16; state++; break; + case 19: foffset |= *p++ << 24; + /* now we have received 20 bytes of potential cab header. */ + /* work out the offset in the file of this potential cabinet */ + caboff = offset + (p-pstart) - 20; + + /* check that the files offset is less than the alleged length + * of the cabinet, and that the offset + the alleged length are + * 'roughly' within the end of overall file length + */ + if ((foffset < cablen) && + ((caboff + foffset) < (filelen + 32)) && + ((caboff + cablen) < (filelen + 32)) ) + { + /* found a potential result - try loading it */ + found++; + cab2 = load_cab_offset(name, caboff); + if (cab2) { + /* success */ + ok++; + + /* cause the search to restart after this cab's data. */ + offset = caboff + cablen; + if (offset < cab->filelen) cabinet_seek(cab, offset); + length = 0; + p = pend; + + /* link the cab into the list */ + if (linkcab == NULL) firstcab = cab2; + else linkcab->next = cab2; + linkcab = cab2; + } + } + state = 0; + break; + default: + p++, state++; break; + } + } + } + cabinet_close(cab); + } + free(cab); + } + + /* if there were cabinets that were found but are not ok, point this out */ + if (found > ok) { + WARN("%s: found %d bad cabinets\n", debugstr_a(name), found-ok); + } + + /* if no cabinets were found, let the user know */ + if (!firstcab) { + WARN("%s: not a Microsoft cabinet file.\n", debugstr_a(name)); + } + return firstcab; +} + +/*********************************************************************** + * find_cabinet_file (internal) + * + * tries to find *cabname, from the directory path of origcab, correcting the + * case of *cabname if necessary, If found, writes back to *cabname. + */ +void find_cabinet_file(char **cabname, LPCSTR origcab) { + + char *tail, *cab, *name, *nextpart, nametmp[MAX_PATH], *filepart; + int found = 0; + + TRACE("(*cabname == ^%p, origcab == %s)\n", cabname ? *cabname : NULL, debugstr_a(origcab)); + + /* ensure we have a cabinet name at all */ + if (!(name = *cabname)) { + WARN("no cabinet name at all\n"); + } + + /* find if there's a directory path in the origcab */ + tail = origcab ? max(strrchr(origcab, '/'), strrchr(origcab, '\\')) : NULL; + + if ((cab = (char *) malloc(MAX_PATH))) { + /* add the directory path from the original cabinet name */ + if (tail) { + memcpy(cab, origcab, tail - origcab); + cab[tail - origcab] = '\0'; + } else { + /* default directory path of '.' */ + cab[0] = '.'; + cab[1] = '\0'; + } + + do { + TRACE("trying cab == %s\n", debugstr_a(cab)); + + /* we don't want null cabinet filenames */ + if (name[0] == '\0') { + WARN("null cab name\n"); + break; + } + + /* if there is a directory component in the cabinet name, + * look for that alone first + */ + nextpart = strchr(name, '\\'); + if (nextpart) *nextpart = '\0'; + + found = SearchPathA(cab, name, NULL, MAX_PATH, nametmp, &filepart); + + /* if the component was not found, look for it in the current dir */ + if (!found) { + found = SearchPathA(".", name, NULL, MAX_PATH, nametmp, &filepart); + } + + if (found) + TRACE("found: %s\n", debugstr_a(nametmp)); + else + TRACE("not found.\n"); + + /* restore the real name and skip to the next directory component + * or actual cabinet name + */ + if (nextpart) *nextpart = '\\', name = &nextpart[1]; + + /* while there is another directory component, and while we + * successfully found the current component + */ + } while (nextpart && found); + + /* if we found the cabinet, change the next cabinet's name. + * otherwise, pretend nothing happened + */ + if (found) { + free((void *) *cabname); + *cabname = cab; + strncpy(cab, nametmp, found+1); + TRACE("result: %s\n", debugstr_a(cab)); + } else { + free((void *) cab); + TRACE("result: nothing\n"); + } + } +} + +/************************************************************************ + * process_files (internal) + * + * this does the tricky job of running through every file in the cabinet, + * including spanning cabinets, and working out which file is in which + * folder in which cabinet. It also throws out the duplicate file entries + * that appear in spanning cabinets. There is memory leakage here because + * those entries are not freed. See the XAD CAB client (function CAB_GetInfo + * in CAB.c) for an implementation of this that correctly frees the discarded + * file entries. + */ +struct cab_file *process_files(struct cabinet *basecab) { + struct cabinet *cab; + struct cab_file *outfi = NULL, *linkfi = NULL, *nextfi, *fi, *cfi; + struct cab_folder *fol, *firstfol, *lastfol = NULL, *predfol; + int i, mergeok; + + FIXME("(basecab == ^%p): Memory leak.\n", basecab); + + for (cab = basecab; cab; cab = cab->nextcab) { + /* firstfol = first folder in this cabinet */ + /* lastfol = last folder in this cabinet */ + /* predfol = last folder in previous cabinet (or NULL if first cabinet) */ + predfol = lastfol; + firstfol = cab->folders; + for (lastfol = firstfol; lastfol->next;) lastfol = lastfol->next; + mergeok = 1; + + for (fi = cab->files; fi; fi = nextfi) { + i = fi->index; + nextfi = fi->next; + + if (i < cffileCONTINUED_FROM_PREV) { + for (fol = firstfol; fol && i--; ) fol = fol->next; + fi->folder = fol; /* NULL if an invalid folder index */ + } + else { + /* folder merging */ + if (i == cffileCONTINUED_TO_NEXT + || i == cffileCONTINUED_PREV_AND_NEXT) { + if (cab->nextcab && !lastfol->contfile) lastfol->contfile = fi; + } + + if (i == cffileCONTINUED_FROM_PREV + || i == cffileCONTINUED_PREV_AND_NEXT) { + /* these files are to be continued in yet another + * cabinet, don't merge them in just yet */ + if (i == cffileCONTINUED_PREV_AND_NEXT) mergeok = 0; + + /* only merge once per cabinet */ + if (predfol) { + if ((cfi = predfol->contfile) + && (cfi->offset == fi->offset) + && (cfi->length == fi->length) + && (strcmp(cfi->filename, fi->filename) == 0) + && (predfol->comp_type == firstfol->comp_type)) { + /* increase the number of splits */ + if ((i = ++(predfol->num_splits)) > CAB_SPLITMAX) { + mergeok = 0; + ERR("%s: internal error: CAB_SPLITMAX exceeded. please report this to wine-devel@winehq.org)\n", + debugstr_a(basecab->filename)); + } + else { + /* copy information across from the merged folder */ + predfol->offset[i] = firstfol->offset[0]; + predfol->cab[i] = firstfol->cab[0]; + predfol->next = firstfol->next; + predfol->contfile = firstfol->contfile; + + if (firstfol == lastfol) lastfol = predfol; + firstfol = predfol; + predfol = NULL; /* don't merge again within this cabinet */ + } + } + else { + /* if the folders won't merge, don't add their files */ + mergeok = 0; + } + } + + if (mergeok) fi->folder = firstfol; + } + } + + if (fi->folder) { + if (linkfi) linkfi->next = fi; else outfi = fi; + linkfi = fi; + } + } /* for (fi= .. */ + } /* for (cab= ...*/ + + return outfi; +} + +/**************************************************************** + * convertUTF (internal) + * + * translate UTF -> ASCII + * + * UTF translates two-byte unicode characters into 1, 2 or 3 bytes. + * %000000000xxxxxxx -> %0xxxxxxx + * %00000xxxxxyyyyyy -> %110xxxxx %10yyyyyy + * %xxxxyyyyyyzzzzzz -> %1110xxxx %10yyyyyy %10zzzzzz + * + * Therefore, the inverse is as follows: + * First char: + * 0x00 - 0x7F = one byte char + * 0x80 - 0xBF = invalid + * 0xC0 - 0xDF = 2 byte char (next char only 0x80-0xBF is valid) + * 0xE0 - 0xEF = 3 byte char (next 2 chars only 0x80-0xBF is valid) + * 0xF0 - 0xFF = invalid + * + * FIXME: use a winapi to do this + */ +int convertUTF(cab_UBYTE *in) { + cab_UBYTE c, *out = in, *end = in + strlen((char *) in) + 1; + cab_ULONG x; + + do { + /* read unicode character */ + if ((c = *in++) < 0x80) x = c; + else { + if (c < 0xC0) return 0; + else if (c < 0xE0) { + x = (c & 0x1F) << 6; + if ((c = *in++) < 0x80 || c > 0xBF) return 0; else x |= (c & 0x3F); + } + else if (c < 0xF0) { + x = (c & 0xF) << 12; + if ((c = *in++) < 0x80 || c > 0xBF) return 0; else x |= (c & 0x3F)<<6; + if ((c = *in++) < 0x80 || c > 0xBF) return 0; else x |= (c & 0x3F); + } + else return 0; + } + + /* terrible unicode -> ASCII conversion */ + if (x > 127) x = '_'; + + if (in > end) return 0; /* just in case */ + } while ((*out++ = (cab_UBYTE) x)); + return 1; +} + +/**************************************************** + * NONEdecompress (internal) + */ +int NONEdecompress(int inlen, int outlen, cab_decomp_state *decomp_state) +{ + if (inlen != outlen) return DECR_ILLEGALDATA; + memcpy(CAB(outbuf), CAB(inbuf), (size_t) inlen); + return DECR_OK; +} + +/************************************************** + * checksum (internal) + */ +cab_ULONG checksum(cab_UBYTE *data, cab_UWORD bytes, cab_ULONG csum) { + int len; + cab_ULONG ul = 0; + + for (len = bytes >> 2; len--; data += 4) { + csum ^= ((data[0]) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24)); + } + + switch (bytes & 3) { + case 3: ul |= *data++ << 16; + case 2: ul |= *data++ << 8; + case 1: ul |= *data; + } + csum ^= ul; + + return csum; +} + +/********************************************************** + * decompress (internal) + */ +int decompress(struct cab_file *fi, int savemode, int fix, cab_decomp_state *decomp_state) +{ + cab_ULONG bytes = savemode ? fi->length : fi->offset - CAB(offset); + struct cabinet *cab = CAB(current)->cab[CAB(split)]; + cab_UBYTE buf[cfdata_SIZEOF], *data; + cab_UWORD inlen, len, outlen, cando; + cab_ULONG cksum; + cab_LONG err; + + TRACE("(fi == ^%p, savemode == %d, fix == %d)\n", fi, savemode, fix); + + while (bytes > 0) { + /* cando = the max number of bytes we can do */ + cando = CAB(outlen); + if (cando > bytes) cando = bytes; + + /* if cando != 0 */ + if (cando && savemode) + file_write(fi, CAB(outpos), cando); + + CAB(outpos) += cando; + CAB(outlen) -= cando; + bytes -= cando; if (!bytes) break; + + /* we only get here if we emptied the output buffer */ + + /* read data header + data */ + inlen = outlen = 0; + while (outlen == 0) { + /* read the block header, skip the reserved part */ + if (!cabinet_read(cab, buf, cfdata_SIZEOF)) return DECR_INPUT; + cabinet_skip(cab, cab->block_resv); + + /* we shouldn't get blocks over CAB_INPUTMAX in size */ + data = CAB(inbuf) + inlen; + len = EndGetI16(buf+cfdata_CompressedSize); + inlen += len; + if (inlen > CAB_INPUTMAX) return DECR_INPUT; + if (!cabinet_read(cab, data, len)) return DECR_INPUT; + + /* clear two bytes after read-in data */ + data[len+1] = data[len+2] = 0; + + /* perform checksum test on the block (if one is stored) */ + cksum = EndGetI32(buf+cfdata_CheckSum); + if (cksum && cksum != checksum(buf+4, 4, checksum(data, len, 0))) { + /* checksum is wrong */ + if (fix && ((fi->folder->comp_type & cffoldCOMPTYPE_MASK) + == cffoldCOMPTYPE_MSZIP)) + { + WARN("%s: checksum failed\n", debugstr_a(fi->filename)); + } + else { + return DECR_CHECKSUM; + } + } + + /* outlen=0 means this block was part of a split block */ + outlen = EndGetI16(buf+cfdata_UncompressedSize); + if (outlen == 0) { + cabinet_close(cab); + cab = CAB(current)->cab[++CAB(split)]; + if (!cabinet_open(cab)) return DECR_INPUT; + cabinet_seek(cab, CAB(current)->offset[CAB(split)]); + } + } + + /* decompress block */ + if ((err = CAB(decompress)(inlen, outlen, decomp_state))) { + if (fix && ((fi->folder->comp_type & cffoldCOMPTYPE_MASK) + == cffoldCOMPTYPE_MSZIP)) + { + ERR("%s: failed decrunching block\n", debugstr_a(fi->filename)); + } + else { + return err; + } + } + CAB(outlen) = outlen; + CAB(outpos) = CAB(outbuf); + } + + return DECR_OK; +} + +/**************************************************************** + * extract_file (internal) + * + * workhorse to extract a particular file from a cab + */ +void extract_file(struct cab_file *fi, int lower, int fix, LPCSTR dir, cab_decomp_state *decomp_state) +{ + struct cab_folder *fol = fi->folder, *oldfol = CAB(current); + cab_LONG err = DECR_OK; + + TRACE("(fi == ^%p, lower == %d, fix == %d, dir == %s)\n", fi, lower, fix, debugstr_a(dir)); + + /* is a change of folder needed? do we need to reset the current folder? */ + if (fol != oldfol || fi->offset < CAB(offset)) { + cab_UWORD comptype = fol->comp_type; + int ct1 = comptype & cffoldCOMPTYPE_MASK; + int ct2 = oldfol ? (oldfol->comp_type & cffoldCOMPTYPE_MASK) : 0; + + /* if the archiver has changed, call the old archiver's free() function */ + if (ct1 != ct2) { + switch (ct2) { + case cffoldCOMPTYPE_LZX: + if (LZX(window)) { + free(LZX(window)); + LZX(window) = NULL; + } + break; + case cffoldCOMPTYPE_QUANTUM: + if (QTM(window)) { + free(QTM(window)); + QTM(window) = NULL; + } + break; + } + } + + switch (ct1) { + case cffoldCOMPTYPE_NONE: + CAB(decompress) = NONEdecompress; + break; + + case cffoldCOMPTYPE_MSZIP: + CAB(decompress) = ZIPdecompress; + break; + + case cffoldCOMPTYPE_QUANTUM: + CAB(decompress) = QTMdecompress; + err = QTMinit((comptype >> 8) & 0x1f, (comptype >> 4) & 0xF, decomp_state); + break; + + case cffoldCOMPTYPE_LZX: + CAB(decompress) = LZXdecompress; + err = LZXinit((comptype >> 8) & 0x1f, decomp_state); + break; + + default: + err = DECR_DATAFORMAT; + } + if (err) goto exit_handler; + + /* initialisation OK, set current folder and reset offset */ + if (oldfol) cabinet_close(oldfol->cab[CAB(split)]); + if (!cabinet_open(fol->cab[0])) goto exit_handler; + cabinet_seek(fol->cab[0], fol->offset[0]); + CAB(current) = fol; + CAB(offset) = 0; + CAB(outlen) = 0; /* discard existing block */ + CAB(split) = 0; + } + + if (fi->offset > CAB(offset)) { + /* decode bytes and send them to /dev/null */ + if ((err = decompress(fi, 0, fix, decomp_state))) goto exit_handler; + CAB(offset) = fi->offset; + } + + if (!file_open(fi, lower, dir)) return; + err = decompress(fi, 1, fix, decomp_state); + if (err) CAB(current) = NULL; else CAB(offset) += fi->length; + file_close(fi); + +exit_handler: + if (err) { + const char *errmsg; + const char *cabname; + switch (err) { + case DECR_NOMEMORY: + errmsg = "out of memory!\n"; break; + case DECR_ILLEGALDATA: + errmsg = "%s: illegal or corrupt data\n"; break; + case DECR_DATAFORMAT: + errmsg = "%s: unsupported data format\n"; break; + case DECR_CHECKSUM: + errmsg = "%s: checksum error\n"; break; + case DECR_INPUT: + errmsg = "%s: input error\n"; break; + case DECR_OUTPUT: + errmsg = "%s: output error\n"; break; + default: + errmsg = "%s: unknown error (BUG)\n"; + } + + if (CAB(current)) { + cabname = (CAB(current)->cab[CAB(split)]->filename); + } + else { + cabname = (fi->folder->cab[0]->filename); + } + + ERR((char *)errmsg, cabname); + } +} + +/********************************************************* + * print_fileinfo (internal) + */ +void print_fileinfo(struct cab_file *fi) { + int d = fi->date, t = fi->time; + char *fname = NULL; + + if (fi->attribs & cffile_A_NAME_IS_UTF) { + fname = malloc(strlen(fi->filename) + 1); + if (fname) { + strcpy(fname, fi->filename); + convertUTF((cab_UBYTE *) fname); + } + } + + TRACE("%9u | %02d.%02d.%04d %02d:%02d:%02d | %s\n", + fi->length, + d & 0x1f, (d>>5) & 0xf, (d>>9) + 1980, + t >> 11, (t>>5) & 0x3f, (t << 1) & 0x3e, + fname ? fname : fi->filename + ); + + if (fname) free(fname); +} + +/**************************************************************************** + * process_cabinet (internal) + * + * called to simply "extract" a cabinet file. Will find every cabinet file + * in that file, search for every chained cabinet attached to those cabinets, + * and will either extract the cabinets, or ? (call a callback?) + * + * PARAMS + * cabname [I] name of the cabinet file to extract + * dir [I] directory to extract to + * fix [I] attempt to process broken cabinets + * lower [I] ? (lower case something or other?) + * dest [O] + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ +BOOL process_cabinet(LPCSTR cabname, LPCSTR dir, BOOL fix, BOOL lower, EXTRACTdest *dest) +{ + struct cabinet *basecab, *cab, *cab1, *cab2; + struct cab_file *filelist, *fi; + struct ExtractFileList **destlistptr = &(dest->filelist); + + /* The first result of a search will be returned, and + * the remaining results will be chained to it via the cab->next structure + * member. + */ + cab_UBYTE search_buf[CAB_SEARCH_SIZE]; + + cab_decomp_state decomp_state_local; + cab_decomp_state *decomp_state = &decomp_state_local; + + /* has the list-mode header been seen before? */ + int viewhdr = 0; + + ZeroMemory(decomp_state, sizeof(cab_decomp_state)); + + TRACE("Extract %s\n", debugstr_a(cabname)); + + /* load the file requested */ + basecab = find_cabs_in_file(cabname, search_buf); + if (!basecab) return FALSE; + + /* iterate over all cabinets found in that file */ + for (cab = basecab; cab; cab=cab->next) { + + /* bi-directionally load any spanning cabinets -- backwards */ + for (cab1 = cab; cab1->flags & cfheadPREV_CABINET; cab1 = cab1->prevcab) { + TRACE("%s: extends backwards to %s (%s)\n", debugstr_a(cabname), + debugstr_a(cab1->prevname), debugstr_a(cab1->previnfo)); + find_cabinet_file(&(cab1->prevname), cabname); + if (!(cab1->prevcab = load_cab_offset(cab1->prevname, 0))) { + ERR("%s: can't read previous cabinet %s\n", debugstr_a(cabname), debugstr_a(cab1->prevname)); + break; + } + cab1->prevcab->nextcab = cab1; + } + + /* bi-directionally load any spanning cabinets -- forwards */ + for (cab2 = cab; cab2->flags & cfheadNEXT_CABINET; cab2 = cab2->nextcab) { + TRACE("%s: extends to %s (%s)\n", debugstr_a(cabname), + debugstr_a(cab2->nextname), debugstr_a(cab2->nextinfo)); + find_cabinet_file(&(cab2->nextname), cabname); + if (!(cab2->nextcab = load_cab_offset(cab2->nextname, 0))) { + ERR("%s: can't read next cabinet %s\n", debugstr_a(cabname), debugstr_a(cab2->nextname)); + break; + } + cab2->nextcab->prevcab = cab2; + } + + filelist = process_files(cab1); + CAB(current) = NULL; + + if (!viewhdr) { + TRACE("File size | Date Time | Name\n"); + TRACE("----------+---------------------+-------------\n"); + viewhdr = 1; + } + for (fi = filelist; fi; fi = fi->next) { + print_fileinfo(fi); + dest->filecount++; + } + TRACE("Beginning Extraction...\n"); + for (fi = filelist; fi; fi = fi->next) { + TRACE(" extracting: %s\n", debugstr_a(fi->filename)); + extract_file(fi, lower, fix, dir, decomp_state); + sprintf(dest->lastfile, "%s%s%s", + strlen(dest->directory) ? dest->directory : "", + strlen(dest->directory) ? "\\": "", + fi->filename); + *destlistptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(struct ExtractFileList)); + if(*destlistptr) { + (*destlistptr)->unknown = TRUE; /* FIXME: were do we get the value? */ + (*destlistptr)->filename = HeapAlloc(GetProcessHeap(), 0, ( + strlen(fi->filename)+1)); + if((*destlistptr)->filename) + lstrcpyA((*destlistptr)->filename, fi->filename); + destlistptr = &((*destlistptr)->next); + } + } + } + + TRACE("Finished processing cabinet.\n"); + + return TRUE; +} diff --git a/reactos/lib/cabinet/cabinet.h b/reactos/lib/cabinet/cabinet.h new file mode 100644 index 00000000000..3ef0dc25d4a --- /dev/null +++ b/reactos/lib/cabinet/cabinet.h @@ -0,0 +1,580 @@ +/* + * cabinet.h + * + * Copyright 2002 Greg Turner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __WINE_CABINET_H +#define __WINE_CABINET_H + +#include + +#include "windef.h" +#include "winbase.h" +#include "winnt.h" +#include "fdi.h" +#include "fci.h" + +/* from msvcrt/sys/stat.h */ +#define _S_IWRITE 0x0080 +#define _S_IREAD 0x0100 + +#define CAB_SPLITMAX (10) + +#define CAB_SEARCH_SIZE (32*1024) + +typedef unsigned char cab_UBYTE; /* 8 bits */ +typedef UINT16 cab_UWORD; /* 16 bits */ +typedef UINT32 cab_ULONG; /* 32 bits */ +typedef INT32 cab_LONG; /* 32 bits */ + +typedef UINT32 cab_off_t; + +/* number of bits in a ULONG */ +#ifndef CHAR_BIT +# define CHAR_BIT (8) +#endif +#define CAB_ULONG_BITS (sizeof(cab_ULONG) * CHAR_BIT) + +/* structure offsets */ +#define cfhead_Signature (0x00) +#define cfhead_CabinetSize (0x08) +#define cfhead_FileOffset (0x10) +#define cfhead_MinorVersion (0x18) +#define cfhead_MajorVersion (0x19) +#define cfhead_NumFolders (0x1A) +#define cfhead_NumFiles (0x1C) +#define cfhead_Flags (0x1E) +#define cfhead_SetID (0x20) +#define cfhead_CabinetIndex (0x22) +#define cfhead_SIZEOF (0x24) +#define cfheadext_HeaderReserved (0x00) +#define cfheadext_FolderReserved (0x02) +#define cfheadext_DataReserved (0x03) +#define cfheadext_SIZEOF (0x04) +#define cffold_DataOffset (0x00) +#define cffold_NumBlocks (0x04) +#define cffold_CompType (0x06) +#define cffold_SIZEOF (0x08) +#define cffile_UncompressedSize (0x00) +#define cffile_FolderOffset (0x04) +#define cffile_FolderIndex (0x08) +#define cffile_Date (0x0A) +#define cffile_Time (0x0C) +#define cffile_Attribs (0x0E) +#define cffile_SIZEOF (0x10) +#define cfdata_CheckSum (0x00) +#define cfdata_CompressedSize (0x04) +#define cfdata_UncompressedSize (0x06) +#define cfdata_SIZEOF (0x08) + +/* flags */ +#define cffoldCOMPTYPE_MASK (0x000f) +#define cffoldCOMPTYPE_NONE (0x0000) +#define cffoldCOMPTYPE_MSZIP (0x0001) +#define cffoldCOMPTYPE_QUANTUM (0x0002) +#define cffoldCOMPTYPE_LZX (0x0003) +#define cfheadPREV_CABINET (0x0001) +#define cfheadNEXT_CABINET (0x0002) +#define cfheadRESERVE_PRESENT (0x0004) +#define cffileCONTINUED_FROM_PREV (0xFFFD) +#define cffileCONTINUED_TO_NEXT (0xFFFE) +#define cffileCONTINUED_PREV_AND_NEXT (0xFFFF) +#define cffile_A_RDONLY (0x01) +#define cffile_A_HIDDEN (0x02) +#define cffile_A_SYSTEM (0x04) +#define cffile_A_ARCH (0x20) +#define cffile_A_EXEC (0x40) +#define cffile_A_NAME_IS_UTF (0x80) + +/****************************************************************************/ +/* our archiver information / state */ + +/* MSZIP stuff */ +#define ZIPWSIZE 0x8000 /* window size */ +#define ZIPLBITS 9 /* bits in base literal/length lookup table */ +#define ZIPDBITS 6 /* bits in base distance lookup table */ +#define ZIPBMAX 16 /* maximum bit length of any code */ +#define ZIPN_MAX 288 /* maximum number of codes in any set */ + +struct Ziphuft { + cab_UBYTE e; /* number of extra bits or operation */ + cab_UBYTE b; /* number of bits in this code or subcode */ + union { + cab_UWORD n; /* literal, length base, or distance base */ + struct Ziphuft *t; /* pointer to next level of table */ + } v; +}; + +struct ZIPstate { + cab_ULONG window_posn; /* current offset within the window */ + cab_ULONG bb; /* bit buffer */ + cab_ULONG bk; /* bits in bit buffer */ + cab_ULONG ll[288+32]; /* literal/length and distance code lengths */ + cab_ULONG c[ZIPBMAX+1]; /* bit length count table */ + cab_LONG lx[ZIPBMAX+1]; /* memory for l[-1..ZIPBMAX-1] */ + struct Ziphuft *u[ZIPBMAX]; /* table stack */ + cab_ULONG v[ZIPN_MAX]; /* values in order of bit length */ + cab_ULONG x[ZIPBMAX+1]; /* bit offsets, then code stack */ + cab_UBYTE *inpos; +}; + +/* Quantum stuff */ + +struct QTMmodelsym { + cab_UWORD sym, cumfreq; +}; + +struct QTMmodel { + int shiftsleft, entries; + struct QTMmodelsym *syms; + cab_UWORD tabloc[256]; +}; + +struct QTMstate { + cab_UBYTE *window; /* the actual decoding window */ + cab_ULONG window_size; /* window size (1Kb through 2Mb) */ + cab_ULONG actual_size; /* window size when it was first allocated */ + cab_ULONG window_posn; /* current offset within the window */ + + struct QTMmodel model7; + struct QTMmodelsym m7sym[7+1]; + + struct QTMmodel model4, model5, model6pos, model6len; + struct QTMmodelsym m4sym[0x18 + 1]; + struct QTMmodelsym m5sym[0x24 + 1]; + struct QTMmodelsym m6psym[0x2a + 1], m6lsym[0x1b + 1]; + + struct QTMmodel model00, model40, model80, modelC0; + struct QTMmodelsym m00sym[0x40 + 1], m40sym[0x40 + 1]; + struct QTMmodelsym m80sym[0x40 + 1], mC0sym[0x40 + 1]; +}; + +/* LZX stuff */ + +/* some constants defined by the LZX specification */ +#define LZX_MIN_MATCH (2) +#define LZX_MAX_MATCH (257) +#define LZX_NUM_CHARS (256) +#define LZX_BLOCKTYPE_INVALID (0) /* also blocktypes 4-7 invalid */ +#define LZX_BLOCKTYPE_VERBATIM (1) +#define LZX_BLOCKTYPE_ALIGNED (2) +#define LZX_BLOCKTYPE_UNCOMPRESSED (3) +#define LZX_PRETREE_NUM_ELEMENTS (20) +#define LZX_ALIGNED_NUM_ELEMENTS (8) /* aligned offset tree #elements */ +#define LZX_NUM_PRIMARY_LENGTHS (7) /* this one missing from spec! */ +#define LZX_NUM_SECONDARY_LENGTHS (249) /* length tree #elements */ + +/* LZX huffman defines: tweak tablebits as desired */ +#define LZX_PRETREE_MAXSYMBOLS (LZX_PRETREE_NUM_ELEMENTS) +#define LZX_PRETREE_TABLEBITS (6) +#define LZX_MAINTREE_MAXSYMBOLS (LZX_NUM_CHARS + 50*8) +#define LZX_MAINTREE_TABLEBITS (12) +#define LZX_LENGTH_MAXSYMBOLS (LZX_NUM_SECONDARY_LENGTHS+1) +#define LZX_LENGTH_TABLEBITS (12) +#define LZX_ALIGNED_MAXSYMBOLS (LZX_ALIGNED_NUM_ELEMENTS) +#define LZX_ALIGNED_TABLEBITS (7) + +#define LZX_LENTABLE_SAFETY (64) /* we allow length table decoding overruns */ + +#define LZX_DECLARE_TABLE(tbl) \ + cab_UWORD tbl##_table[(1<pfnalloc) (size)) +#define PFDI_FREE(hfdi, ptr) ((*PFDI_INT(hfdi)->pfnfree) (ptr)) +#define PFDI_OPEN(hfdi, file, flag, mode) ((*PFDI_INT(hfdi)->pfnopen) (file, flag, mode)) +#define PFDI_READ(hfdi, hf, pv, cb) ((*PFDI_INT(hfdi)->pfnread) (hf, pv, cb)) +#define PFDI_WRITE(hfdi, hf, pv, cb) ((*PFDI_INT(hfdi)->pfnwrite) (hf, pv, cb)) +#define PFDI_CLOSE(hfdi, hf) ((*PFDI_INT(hfdi)->pfnclose) (hf)) +#define PFDI_SEEK(hfdi, hf, dist, type) ((*PFDI_INT(hfdi)->pfnseek) (hf, dist, type)) + +#define FCI_INT_MAGIC 0xfcfcfc05 +#define FDI_INT_MAGIC 0xfdfdfd05 + +#define REALLY_IS_FCI(hfci) ( \ + (((void *) hfci) != NULL) && \ + (PFCI_INT(hfci)->FCI_Intmagic == FCI_INT_MAGIC) ) + +#define REALLY_IS_FDI(hfdi) ( \ + (((void *) hfdi) != NULL) && \ + (PFDI_INT(hfdi)->FDI_Intmagic == FDI_INT_MAGIC) ) + +/* + * the rest of these are somewhat kludgy macros which are shared between fdi.c + * and cabextract.c. + */ + +#define ZIPNEEDBITS(n) {while(k<(n)){cab_LONG c=*(ZIP(inpos)++);\ + b|=((cab_ULONG)c)<>=(n);k-=(n);} + +/* endian-neutral reading of little-endian data */ +#define EndGetI32(a) ((((a)[3])<<24)|(((a)[2])<<16)|(((a)[1])<<8)|((a)[0])) +#define EndGetI16(a) ((((a)[1])<<8)|((a)[0])) + +#define CAB(x) (decomp_state->x) +#define ZIP(x) (decomp_state->methods.zip.x) +#define QTM(x) (decomp_state->methods.qtm.x) +#define LZX(x) (decomp_state->methods.lzx.x) +#define DECR_OK (0) +#define DECR_DATAFORMAT (1) +#define DECR_ILLEGALDATA (2) +#define DECR_NOMEMORY (3) +#define DECR_CHECKSUM (4) +#define DECR_INPUT (5) +#define DECR_OUTPUT (6) +#define DECR_USERABORT (7) + +/* Bitstream reading macros (Quantum / normal byte order) + * + * Q_INIT_BITSTREAM should be used first to set up the system + * Q_READ_BITS(var,n) takes N bits from the buffer and puts them in var. + * unlike LZX, this can loop several times to get the + * requisite number of bits. + * Q_FILL_BUFFER adds more data to the bit buffer, if there is room + * for another 16 bits. + * Q_PEEK_BITS(n) extracts (without removing) N bits from the bit + * buffer + * Q_REMOVE_BITS(n) removes N bits from the bit buffer + * + * These bit access routines work by using the area beyond the MSB and the + * LSB as a free source of zeroes. This avoids having to mask any bits. + * So we have to know the bit width of the bitbuffer variable. This is + * defined as ULONG_BITS. + * + * ULONG_BITS should be at least 16 bits. Unlike LZX's Huffman decoding, + * Quantum's arithmetic decoding only needs 1 bit at a time, it doesn't + * need an assured number. Retrieving larger bitstrings can be done with + * multiple reads and fills of the bitbuffer. The code should work fine + * for machines where ULONG >= 32 bits. + * + * Also note that Quantum reads bytes in normal order; LZX is in + * little-endian order. + */ + +#define Q_INIT_BITSTREAM do { bitsleft = 0; bitbuf = 0; } while (0) + +#define Q_FILL_BUFFER do { \ + if (bitsleft <= (CAB_ULONG_BITS - 16)) { \ + bitbuf |= ((inpos[0]<<8)|inpos[1]) << (CAB_ULONG_BITS-16 - bitsleft); \ + bitsleft += 16; inpos += 2; \ + } \ +} while (0) + +#define Q_PEEK_BITS(n) (bitbuf >> (CAB_ULONG_BITS - (n))) +#define Q_REMOVE_BITS(n) ((bitbuf <<= (n)), (bitsleft -= (n))) + +#define Q_READ_BITS(v,n) do { \ + (v) = 0; \ + for (bitsneed = (n); bitsneed; bitsneed -= bitrun) { \ + Q_FILL_BUFFER; \ + bitrun = (bitsneed > bitsleft) ? bitsleft : bitsneed; \ + (v) = ((v) << bitrun) | Q_PEEK_BITS(bitrun); \ + Q_REMOVE_BITS(bitrun); \ + } \ +} while (0) + +#define Q_MENTRIES(model) (QTM(model).entries) +#define Q_MSYM(model,symidx) (QTM(model).syms[(symidx)].sym) +#define Q_MSYMFREQ(model,symidx) (QTM(model).syms[(symidx)].cumfreq) + +/* GET_SYMBOL(model, var) fetches the next symbol from the stated model + * and puts it in var. it may need to read the bitstream to do this. + */ +#define GET_SYMBOL(m, var) do { \ + range = ((H - L) & 0xFFFF) + 1; \ + symf = ((((C - L + 1) * Q_MSYMFREQ(m,0)) - 1) / range) & 0xFFFF; \ + \ + for (i=1; i < Q_MENTRIES(m); i++) { \ + if (Q_MSYMFREQ(m,i) <= symf) break; \ + } \ + (var) = Q_MSYM(m,i-1); \ + \ + range = (H - L) + 1; \ + H = L + ((Q_MSYMFREQ(m,i-1) * range) / Q_MSYMFREQ(m,0)) - 1; \ + L = L + ((Q_MSYMFREQ(m,i) * range) / Q_MSYMFREQ(m,0)); \ + while (1) { \ + if ((L & 0x8000) != (H & 0x8000)) { \ + if ((L & 0x4000) && !(H & 0x4000)) { \ + /* underflow case */ \ + C ^= 0x4000; L &= 0x3FFF; H |= 0x4000; \ + } \ + else break; \ + } \ + L <<= 1; H = (H << 1) | 1; \ + Q_FILL_BUFFER; \ + C = (C << 1) | Q_PEEK_BITS(1); \ + Q_REMOVE_BITS(1); \ + } \ + \ + QTMupdatemodel(&(QTM(m)), i); \ +} while (0) + +/* Bitstream reading macros (LZX / intel little-endian byte order) + * + * INIT_BITSTREAM should be used first to set up the system + * READ_BITS(var,n) takes N bits from the buffer and puts them in var + * + * ENSURE_BITS(n) ensures there are at least N bits in the bit buffer. + * it can guarantee up to 17 bits (i.e. it can read in + * 16 new bits when there is down to 1 bit in the buffer, + * and it can read 32 bits when there are 0 bits in the + * buffer). + * PEEK_BITS(n) extracts (without removing) N bits from the bit buffer + * REMOVE_BITS(n) removes N bits from the bit buffer + * + * These bit access routines work by using the area beyond the MSB and the + * LSB as a free source of zeroes. This avoids having to mask any bits. + * So we have to know the bit width of the bitbuffer variable. + */ + +#define INIT_BITSTREAM do { bitsleft = 0; bitbuf = 0; } while (0) + +/* Quantum reads bytes in normal order; LZX is little-endian order */ +#define ENSURE_BITS(n) \ + while (bitsleft < (n)) { \ + bitbuf |= ((inpos[1]<<8)|inpos[0]) << (CAB_ULONG_BITS-16 - bitsleft); \ + bitsleft += 16; inpos+=2; \ + } + +#define PEEK_BITS(n) (bitbuf >> (CAB_ULONG_BITS - (n))) +#define REMOVE_BITS(n) ((bitbuf <<= (n)), (bitsleft -= (n))) + +#define READ_BITS(v,n) do { \ + if (n) { \ + ENSURE_BITS(n); \ + (v) = PEEK_BITS(n); \ + REMOVE_BITS(n); \ + } \ + else { \ + (v) = 0; \ + } \ +} while (0) + +/* Huffman macros */ + +#define TABLEBITS(tbl) (LZX_##tbl##_TABLEBITS) +#define MAXSYMBOLS(tbl) (LZX_##tbl##_MAXSYMBOLS) +#define SYMTABLE(tbl) (LZX(tbl##_table)) +#define LENTABLE(tbl) (LZX(tbl##_len)) + +/* BUILD_TABLE(tablename) builds a huffman lookup table from code lengths. + * In reality, it just calls make_decode_table() with the appropriate + * values - they're all fixed by some #defines anyway, so there's no point + * writing each call out in full by hand. + */ +#define BUILD_TABLE(tbl) \ + if (make_decode_table( \ + MAXSYMBOLS(tbl), TABLEBITS(tbl), LENTABLE(tbl), SYMTABLE(tbl) \ + )) { return DECR_ILLEGALDATA; } + +/* READ_HUFFSYM(tablename, var) decodes one huffman symbol from the + * bitstream using the stated table and puts it in var. + */ +#define READ_HUFFSYM(tbl,var) do { \ + ENSURE_BITS(16); \ + hufftbl = SYMTABLE(tbl); \ + if ((i = hufftbl[PEEK_BITS(TABLEBITS(tbl))]) >= MAXSYMBOLS(tbl)) { \ + j = 1 << (CAB_ULONG_BITS - TABLEBITS(tbl)); \ + do { \ + j >>= 1; i <<= 1; i |= (bitbuf & j) ? 1 : 0; \ + if (!j) { return DECR_ILLEGALDATA; } \ + } while ((i = hufftbl[i]) >= MAXSYMBOLS(tbl)); \ + } \ + j = LENTABLE(tbl)[(var) = i]; \ + REMOVE_BITS(j); \ +} while (0) + +/* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols + * first to last in the given table. The code lengths are stored in their + * own special LZX way. + */ +#define READ_LENGTHS(tbl,first,last,fn) do { \ + lb.bb = bitbuf; lb.bl = bitsleft; lb.ip = inpos; \ + if (fn(LENTABLE(tbl),(first),(last),&lb,decomp_state)) { \ + return DECR_ILLEGALDATA; \ + } \ + bitbuf = lb.bb; bitsleft = lb.bl; inpos = lb.ip; \ +} while (0) + +/* Tables for deflate from PKZIP's appnote.txt. */ + +#define THOSE_ZIP_CONSTS \ +static const cab_UBYTE Zipborder[] = /* Order of the bit length code lengths */ \ +{ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; \ +static const cab_UWORD Zipcplens[] = /* Copy lengths for literal codes 257..285 */ \ +{ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, \ + 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; \ +static const cab_UWORD Zipcplext[] = /* Extra bits for literal codes 257..285 */ \ +{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, \ + 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */ \ +static const cab_UWORD Zipcpdist[] = /* Copy offsets for distance codes 0..29 */ \ +{ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, \ +513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; \ +static const cab_UWORD Zipcpdext[] = /* Extra bits for distance codes */ \ +{ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, \ +10, 11, 11, 12, 12, 13, 13}; \ +/* And'ing with Zipmask[n] masks the lower n bits */ \ +static const cab_UWORD Zipmask[17] = { \ + 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, \ + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff \ +} + +struct ExtractFileList { + LPSTR filename; + struct ExtractFileList *next; + BOOL unknown; /* always 1L */ +} ; + +/* the first parameter of the function extract */ +typedef struct { + long result1; /* 0x000 */ + long unknown1[3]; /* 0x004 */ + struct ExtractFileList *filelist; /* 0x010 */ + long filecount; /* 0x014 */ + long unknown2; /* 0x018 */ + char directory[0x104]; /* 0x01c */ + char lastfile[0x20c]; /* 0x120 */ +} EXTRACTdest; + + +/* from cabextract.c */ +BOOL process_cabinet(LPCSTR cabname, LPCSTR dir, BOOL fix, BOOL lower, EXTRACTdest *dest); +void QTMupdatemodel(struct QTMmodel *model, int sym); +int make_decode_table(cab_ULONG nsyms, cab_ULONG nbits, cab_UBYTE *length, cab_UWORD *table); +cab_ULONG checksum(cab_UBYTE *data, cab_UWORD bytes, cab_ULONG csum); + +#endif /* __WINE_CABINET_H */ diff --git a/reactos/lib/cabinet/cabinet.rc b/reactos/lib/cabinet/cabinet.rc new file mode 100644 index 00000000000..6d936b3e304 --- /dev/null +++ b/reactos/lib/cabinet/cabinet.rc @@ -0,0 +1,28 @@ +/* Language neutral resources. + * + * Copyright 2003 Stefan Leichter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define WINE_FILEDESCRIPTION_STR "Wine Cabinet File API" +#define WINE_FILENAME_STR "cabinet.dll" +#define WINE_FILEVERSION 5,0,2147,1 +#define WINE_FILEVERSION_STR "5.0.2147.1" + +#define WINE_PRODUCTVERSION 5,0,2147,1 +#define WINE_PRODUCTVERSION_STR "5.0" + +#include "wine/wine_common_ver.rc" diff --git a/reactos/lib/cabinet/cabinet.spec b/reactos/lib/cabinet/cabinet.spec new file mode 100644 index 00000000000..ff501800dbf --- /dev/null +++ b/reactos/lib/cabinet/cabinet.spec @@ -0,0 +1,14 @@ +1 stub GetDllVersion +2 stdcall DllGetVersion (ptr) CABINET_DllGetVersion +3 stdcall Extract(ptr str) +4 stub DeleteExtractedFiles +10 cdecl FCICreate(ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr) +11 cdecl FCIAddFile(long ptr ptr long ptr ptr ptr long) +12 cdecl FCIFlushFolder(long ptr ptr) +13 cdecl FCIFlushCabinet(long long ptr ptr) +14 cdecl FCIDestroy(long) +20 cdecl FDICreate(ptr ptr ptr ptr ptr ptr ptr long ptr) +21 cdecl FDIIsCabinet(long long ptr) +22 cdecl FDICopy(long ptr ptr long ptr ptr ptr) +23 cdecl FDIDestroy(long) +24 cdecl FDITruncateCabinet(long ptr long) diff --git a/reactos/lib/cabinet/cabinet_main.c b/reactos/lib/cabinet/cabinet_main.c new file mode 100644 index 00000000000..8a46c59f2e9 --- /dev/null +++ b/reactos/lib/cabinet/cabinet_main.c @@ -0,0 +1,133 @@ +/* + * cabinet.dll main + * + * Copyright 2002 Patrik Stridvall + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#define NO_SHLWAPI_REG +#include "shlwapi.h" +#undef NO_SHLWAPI_REG + +#include "cabinet.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(cabinet); + +/*********************************************************************** + * DllGetVersion (CABINET.2) + * + * Retrieves version information of the 'CABINET.DLL' + * + * PARAMS + * pdvi [O] pointer to version information structure. + * + * RETURNS + * Success: S_OK + * Failure: E_INVALIDARG + * + * NOTES + * Supposedly returns version from IE6SP1RP1 + */ +HRESULT WINAPI CABINET_DllGetVersion (DLLVERSIONINFO *pdvi) +{ + WARN("hmmm... not right version number \"5.1.1106.1\"?\n"); + + if (pdvi->cbSize != sizeof(DLLVERSIONINFO)) return E_INVALIDARG; + + pdvi->dwMajorVersion = 5; + pdvi->dwMinorVersion = 1; + pdvi->dwBuildNumber = 1106; + pdvi->dwPlatformID = 1; + + return S_OK; +} + +/*********************************************************************** + * Extract (CABINET.3) + * + * Apparently an undocumented function, presumably to extract a CAB file + * to somewhere... + * + * PARAMS + * dest pointer to a buffer of 0x32c bytes containing + * [I] - number with value 1 at index 0x18 + * - the dest path starting at index 0x1c + * [O] - a linked list with the filename existing inside the + * CAB file at idx 0x10 + * - the number of files inside the CAB file at index 0x14 + * - the name of the last file with dest path at idx 0x120 + * what [I] char* describing what to uncompress, I guess. + * + * RETURNS + * Success: S_OK + * Failure: E_OUTOFMEMORY (?) + */ +HRESULT WINAPI Extract(EXTRACTdest *dest, LPCSTR what) +{ +#define DUMPC(idx) idx >= sizeof(EXTRACTdest) ? ' ' : \ + ptr[idx] >= 0x20 ? ptr[idx] : '.' + +#define DUMPH(idx) idx >= sizeof(EXTRACTdest) ? 0x55 : ptr[idx] + + LPSTR dir; + unsigned char *ptr = (unsigned char*) dest; + unsigned int i; + + TRACE("(dest == %0lx, what == %s)\n", (long) dest, debugstr_a(what)); + + if (!dest) { + /* win2k will crash here */ + FIXME("called without valid parameter dest!\n"); + return E_OUTOFMEMORY; + } + for (i=0; i < sizeof(EXTRACTdest); i+=8) + TRACE( "dest[%04x]:%02x %02x %02x %02x %02x %02x %02x %02x %c%c%c%c%c%c%c%c\n", + i, + DUMPH(i+0), DUMPH(i+1), DUMPH(i+2), DUMPH(i+3), + DUMPH(i+4), DUMPH(i+5), DUMPH(i+6), DUMPH(i+7), + DUMPC(i+0), DUMPC(i+1), DUMPC(i+2), DUMPC(i+3), + DUMPC(i+4), DUMPC(i+5), DUMPC(i+6), DUMPC(i+7)); + + dir = LocalAlloc(LPTR, strlen(dest->directory)+1); + if (!dir) return E_OUTOFMEMORY; + lstrcpyA(dir, dest->directory); + dest->filecount=0; + dest->filelist = NULL; + + TRACE("extracting to dir: %s\n", debugstr_a(dir)); + + /* FIXME: what to do on failure? */ + if (!process_cabinet(what, dir, FALSE, FALSE, dest)) + return E_OUTOFMEMORY; + + LocalFree(dir); + + TRACE("filecount %08lx,lastfile %s\n", + dest->filecount, debugstr_a(dest->lastfile)); + + return S_OK; +} diff --git a/reactos/lib/cabinet/fci.c b/reactos/lib/cabinet/fci.c new file mode 100644 index 00000000000..4fd9476c66c --- /dev/null +++ b/reactos/lib/cabinet/fci.c @@ -0,0 +1,127 @@ +/* + * File Compression Interface + * + * Copyright 2002 Patrik Stridvall + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "fci.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(cabinet); + +/*********************************************************************** + * FCICreate (CABINET.10) + */ +HFCI __cdecl FCICreate( + PERF perf, + PFNFCIFILEPLACED pfnfcifp, + PFNFCIALLOC pfna, + PFNFCIFREE pfnf, + PFNFCIOPEN pfnopen, + PFNFCIREAD pfnread, + PFNFCIWRITE pfnwrite, + PFNFCICLOSE pfnclose, + PFNFCISEEK pfnseek, + PFNFCIDELETE pfndelete, + PFNFCIGETTEMPFILE pfnfcigtf, + PCCAB pccab, + void *pv) +{ + FIXME("(%p, %p, %p, %p, %p, %p, %p, %p, %p, %p, %p, %p, %p): stub\n", + perf, pfnfcifp, pfna, pfnf, pfnopen, pfnread, pfnwrite, pfnclose, + pfnseek, pfndelete, pfnfcigtf, pccab, pv); + + perf->erfOper = FCIERR_NONE; + perf->erfType = 0; + perf->fError = TRUE; + + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + + return NULL; +} + +/*********************************************************************** + * FCIAddFile (CABINET.11) + */ +BOOL __cdecl FCIAddFile( + HFCI hfci, + char *pszSourceFile, + char *pszFileName, + BOOL fExecute, + PFNFCIGETNEXTCABINET pfnfcignc, + PFNFCISTATUS pfnfcis, + PFNFCIGETOPENINFO pfnfcigoi, + TCOMP typeCompress) +{ + FIXME("(%p, %p, %p, %d, %p, %p, %p, %hu): stub\n", hfci, pszSourceFile, + pszFileName, fExecute, pfnfcignc, pfnfcis, pfnfcigoi, typeCompress); + + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + + return FALSE; +} + +/*********************************************************************** + * FCIFlushCabinet (CABINET.13) + */ +BOOL __cdecl FCIFlushCabinet( + HFCI hfci, + BOOL fGetNextCab, + PFNFCIGETNEXTCABINET pfnfcignc, + PFNFCISTATUS pfnfcis) +{ + FIXME("(%p, %d, %p, %p): stub\n", hfci, fGetNextCab, pfnfcignc, pfnfcis); + + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + + return FALSE; +} + +/*********************************************************************** + * FCIFlushFolder (CABINET.12) + */ +BOOL __cdecl FCIFlushFolder( + HFCI hfci, + PFNFCIGETNEXTCABINET pfnfcignc, + PFNFCISTATUS pfnfcis) +{ + FIXME("(%p, %p, %p): stub\n", hfci, pfnfcignc, pfnfcis); + + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + + return FALSE; +} + +/*********************************************************************** + * FCIDestroy (CABINET.14) + */ +BOOL __cdecl FCIDestroy(HFCI hfci) +{ + FIXME("(%p): stub\n", hfci); + + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + + return FALSE; +} diff --git a/reactos/lib/cabinet/fdi.c b/reactos/lib/cabinet/fdi.c new file mode 100644 index 00000000000..b845c7cf524 --- /dev/null +++ b/reactos/lib/cabinet/fdi.c @@ -0,0 +1,2839 @@ +/* + * File Decompression Interface + * + * Copyright 2000-2002 Stuart Caie + * Copyright 2002 Patrik Stridvall + * Copyright 2003 Greg Turner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * This is a largely redundant reimplementation of the stuff in cabextract.c. It + * would be theoretically preferable to have only one, shared implementation, however + * there are semantic differences which may discourage efforts to unify the two. It + * should be possible, if awkward, to go back and reimplement cabextract.c using FDI. + * But this approach would be quite a bit less performant. Probably a better way + * would be to create a "library" of routines in cabextract.c which do the actual + * decompression, and have both fdi.c and cabextract share those routines. The rest + * of the code is not sufficiently similar to merit a shared implementation. + * + * The worst thing about this API is the bug. "The bug" is this: when you extract a + * cabinet, it /always/ informs you (via the hasnext field of PFDICABINETINFO), that + * there is no subsequent cabinet, even if there is one. wine faithfully reproduces + * this behavior. + * + * TODO: + * + * Wine does not implement the AFAIK undocumented "enumerate" callback during + * FDICopy. It is implemented in Windows and therefore worth investigating... + * + * Lots of pointers flying around here... am I leaking RAM? + * + * WTF is FDITruncate? + * + * Probably, I need to weed out some dead code-paths. + * + * Test unit(s). + * + * The fdintNEXT_CABINET callbacks are probably not working quite as they should. + * There are several FIXME's in the source describing some of the deficiencies in + * some detail. Additionally, we do not do a very good job of returning the right + * error codes to this callback. + * + * FDICopy and fdi_decomp are incomprehensibly large; separating these into smaller + * functions would be nice. + * + * -gmt + */ + +#include "config.h" + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "fdi.h" +#include "cabinet.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(cabinet); + +THOSE_ZIP_CONSTS; + +struct fdi_file { + struct fdi_file *next; /* next file in sequence */ + LPCSTR filename; /* output name of file */ + int fh; /* open file handle or NULL */ + cab_ULONG length; /* uncompressed length of file */ + cab_ULONG offset; /* uncompressed offset in folder */ + cab_UWORD index; /* magic index number of folder */ + cab_UWORD time, date, attribs; /* MS-DOS time/date/attributes */ + BOOL oppressed; /* never to be processed */ +}; + +struct fdi_folder { + struct fdi_folder *next; + cab_off_t offset; /* offset to data blocks (32 bit) */ + cab_UWORD comp_type; /* compression format/window size */ + cab_ULONG comp_size; /* compressed size of folder */ + cab_UBYTE num_splits; /* number of split blocks + 1 */ + cab_UWORD num_blocks; /* total number of blocks */ +}; + +/* + * this structure fills the gaps between what is available in a PFDICABINETINFO + * vs what is needed by FDICopy. Memory allocated for these becomes the responsibility + * of the caller to free. Yes, I am aware that this is totally, utterly inelegant. + * To make things even more unnecessarily confusing, we now attach these to the + * fdi_decomp_state. + */ +typedef struct { + char *prevname, *previnfo; + char *nextname, *nextinfo; + BOOL hasnext; /* bug free indicator */ + int folder_resv, header_resv; + cab_UBYTE block_resv; +} MORE_ISCAB_INFO, *PMORE_ISCAB_INFO; + +/* + * ugh, well, this ended up being pretty damn silly... + * now that I've conceded to build equivalent structures to struct cab.*, + * I should have just used those, or, better yet, unified the two... sue me. + * (Note to Microsoft: That's a joke. Please /don't/ actually sue me! -gmt). + * Nevertheless, I've come this far, it works, so I'm not gonna change it + * for now. This implementation has significant semantic differences anyhow. + */ + +typedef struct fdi_cds_fwd { + void *hfdi; /* the hfdi we are using */ + int filehf, cabhf; /* file handle we are using */ + struct fdi_folder *current; /* current folder we're extracting from */ + cab_ULONG offset; /* uncompressed offset within folder */ + cab_UBYTE *outpos; /* (high level) start of data to use up */ + cab_UWORD outlen; /* (high level) amount of data to use up */ + int (*decompress)(int, int, struct fdi_cds_fwd *); /* chosen compress fn */ + cab_UBYTE inbuf[CAB_INPUTMAX+2]; /* +2 for lzx bitbuffer overflows! */ + cab_UBYTE outbuf[CAB_BLOCKMAX]; + union { + struct ZIPstate zip; + struct QTMstate qtm; + struct LZXstate lzx; + } methods; + /* some temp variables for use during decompression */ + cab_UBYTE q_length_base[27], q_length_extra[27], q_extra_bits[42]; + cab_ULONG q_position_base[42]; + cab_ULONG lzx_position_base[51]; + cab_UBYTE extra_bits[51]; + USHORT setID; /* Cabinet set ID */ + USHORT iCabinet; /* Cabinet number in set (0 based) */ + struct fdi_cds_fwd *decomp_cab; + MORE_ISCAB_INFO mii; + struct fdi_folder *firstfol; + struct fdi_file *firstfile; + struct fdi_cds_fwd *next; +} fdi_decomp_state; + +/*********************************************************************** + * FDICreate (CABINET.20) + * + * Provided with several callbacks (all of them are mandatory), + * returns a handle which can be used to perform operations + * on cabinet files. + * + * PARAMS + * pfnalloc [I] A pointer to a function which allocates ram. Uses + * the same interface as malloc. + * pfnfree [I] A pointer to a function which frees ram. Uses the + * same interface as free. + * pfnopen [I] A pointer to a function which opens a file. Uses + * the same interface as _open. + * pfnread [I] A pointer to a function which reads from a file into + * a caller-provided buffer. Uses the same interface + * as _read + * pfnwrite [I] A pointer to a function which writes to a file from + * a caller-provided buffer. Uses the same interface + * as _write. + * pfnclose [I] A pointer to a function which closes a file handle. + * Uses the same interface as _close. + * pfnseek [I] A pointer to a function which seeks in a file. + * Uses the same interface as _lseek. + * cpuType [I] The type of CPU; ignored in wine (recommended value: + * cpuUNKNOWN, aka -1). + * perf [IO] A pointer to an ERF structure. When FDICreate + * returns an error condition, error information may + * be found here as well as from GetLastError. + * + * RETURNS + * On success, returns an FDI handle of type HFDI. + * On failure, the NULL file handle is returned. Error + * info can be retrieved from perf. + * + * INCLUDES + * fdi.h + * + */ +HFDI __cdecl FDICreate( + PFNALLOC pfnalloc, + PFNFREE pfnfree, + PFNOPEN pfnopen, + PFNREAD pfnread, + PFNWRITE pfnwrite, + PFNCLOSE pfnclose, + PFNSEEK pfnseek, + int cpuType, + PERF perf) +{ + HFDI rv; + + TRACE("(pfnalloc == ^%p, pfnfree == ^%p, pfnopen == ^%p, pfnread == ^%p, pfnwrite == ^%p, \ + pfnclose == ^%p, pfnseek == ^%p, cpuType == %d, perf == ^%p)\n", + pfnalloc, pfnfree, pfnopen, pfnread, pfnwrite, pfnclose, pfnseek, + cpuType, perf); + + if ((!pfnalloc) || (!pfnfree)) { + perf->erfOper = FDIERROR_NONE; + perf->erfType = ERROR_BAD_ARGUMENTS; + perf->fError = TRUE; + + SetLastError(ERROR_BAD_ARGUMENTS); + return NULL; + } + + if (!((rv = ((HFDI) (*pfnalloc)(sizeof(FDI_Int)))))) { + perf->erfOper = FDIERROR_ALLOC_FAIL; + perf->erfType = ERROR_NOT_ENOUGH_MEMORY; + perf->fError = TRUE; + + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + PFDI_INT(rv)->FDI_Intmagic = FDI_INT_MAGIC; + PFDI_INT(rv)->pfnalloc = pfnalloc; + PFDI_INT(rv)->pfnfree = pfnfree; + PFDI_INT(rv)->pfnopen = pfnopen; + PFDI_INT(rv)->pfnread = pfnread; + PFDI_INT(rv)->pfnwrite = pfnwrite; + PFDI_INT(rv)->pfnclose = pfnclose; + PFDI_INT(rv)->pfnseek = pfnseek; + /* no-brainer: we ignore the cpu type; this is only used + for the 16-bit versions in Windows anyhow... */ + PFDI_INT(rv)->perf = perf; + + return rv; +} + +/******************************************************************* + * FDI_getoffset (internal) + * + * returns the file pointer position of a file handle. + */ +long FDI_getoffset(HFDI hfdi, INT_PTR hf) +{ + return PFDI_SEEK(hfdi, hf, 0L, SEEK_CUR); +} + +/********************************************************************** + * FDI_realloc (internal) + * + * we can't use _msize; the user might not be using malloc, so we require + * an explicit specification of the previous size. inefficient. + */ +void *FDI_realloc(HFDI hfdi, void *mem, size_t prevsize, size_t newsize) +{ + void *rslt = NULL; + char *irslt, *imem; + size_t copysize = (prevsize < newsize) ? prevsize : newsize; + if (prevsize == newsize) return mem; + rslt = PFDI_ALLOC(hfdi, newsize); + if (rslt) + for (irslt = (char *)rslt, imem = (char *)mem; (copysize); copysize--) + *irslt++ = *imem++; + PFDI_FREE(hfdi, mem); + return rslt; +} + +/********************************************************************** + * FDI_read_string (internal) + * + * allocate and read an arbitrarily long string from the cabinet + */ +char *FDI_read_string(HFDI hfdi, INT_PTR hf, long cabsize) +{ + size_t len=256, + oldlen = 0, + base = FDI_getoffset(hfdi, hf), + maxlen = cabsize - base; + BOOL ok = FALSE; + unsigned int i; + cab_UBYTE *buf = NULL; + + TRACE("(hfdi == ^%p, hf == %d)\n", hfdi, hf); + + do { + if (len > maxlen) len = maxlen; + if (!(buf = FDI_realloc(hfdi, buf, oldlen, len))) break; + oldlen = len; + if (!PFDI_READ(hfdi, hf, buf, len)) break; + + /* search for a null terminator in what we've just read */ + for (i=0; i < len; i++) { + if (!buf[i]) {ok=TRUE; break;} + } + + if (!ok) { + if (len == maxlen) { + ERR("cabinet is truncated\n"); + break; + } + len += 256; + PFDI_SEEK(hfdi, hf, base, SEEK_SET); + } + } while (!ok); + + if (!ok) { + if (buf) + PFDI_FREE(hfdi, buf); + else + ERR("out of memory!\n"); + return NULL; + } + + /* otherwise, set the stream to just after the string and return */ + PFDI_SEEK(hfdi, hf, base + ((cab_off_t) strlen((char *) buf)) + 1, SEEK_SET); + + return (char *) buf; +} + +/****************************************************************** + * FDI_read_entries (internal) + * + * process the cabinet header in the style of FDIIsCabinet, but + * without the sanity checks (and bug) + */ +BOOL FDI_read_entries( + HFDI hfdi, + INT_PTR hf, + PFDICABINETINFO pfdici, + PMORE_ISCAB_INFO pmii) +{ + int num_folders, num_files, header_resv, folder_resv = 0; + LONG base_offset, cabsize; + USHORT setid, cabidx, flags; + cab_UBYTE buf[64], block_resv; + char *prevname = NULL, *previnfo = NULL, *nextname = NULL, *nextinfo = NULL; + + TRACE("(hfdi == ^%p, hf == %d, pfdici == ^%p)\n", hfdi, hf, pfdici); + + /* + * FIXME: I just noticed that I am memorizing the initial file pointer + * offset and restoring it before reading in the rest of the header + * information in the cabinet. Perhaps that's correct -- that is, perhaps + * this API is supposed to support "streaming" cabinets which are embedded + * in other files, or cabinets which begin at file offsets other than zero. + * Otherwise, I should instead go to the absolute beginning of the file. + * (Either way, the semantics of wine's FDICopy require me to leave the + * file pointer where it is afterwards -- If Windows does not do so, we + * ought to duplicate the native behavior in the FDIIsCabinet API, not here. + * + * So, the answer lies in Windows; will native cabinet.dll recognize a + * cabinet "file" embedded in another file? Note that cabextract.c does + * support this, which implies that Microsoft's might. I haven't tried it + * yet so I don't know. ATM, most of wine's FDI cabinet routines (except + * this one) would not work in this way. To fix it, we could just make the + * various references to absolute file positions in the code relative to an + * initial "beginning" offset. Because the FDICopy API doesn't take a + * file-handle like this one, we would therein need to search through the + * file for the beginning of the cabinet (as we also do in cabextract.c). + * Note that this limits us to a maximum of one cabinet per. file: the first. + * + * So, in summary: either the code below is wrong, or the rest of fdi.c is + * wrong... I cannot imagine that both are correct ;) One of these flaws + * should be fixed after determining the behavior on Windows. We ought + * to check both FDIIsCabinet and FDICopy for the right behavior. + * + * -gmt + */ + + /* get basic offset & size info */ + base_offset = FDI_getoffset(hfdi, hf); + + if (PFDI_SEEK(hfdi, hf, 0, SEEK_END) == -1) { + if (pmii) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_NOT_A_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + } + return FALSE; + } + + cabsize = FDI_getoffset(hfdi, hf); + + if ((cabsize == -1) || (base_offset == -1) || + ( PFDI_SEEK(hfdi, hf, base_offset, SEEK_SET) == -1 )) { + if (pmii) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_NOT_A_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + } + return FALSE; + } + + /* read in the CFHEADER */ + if (PFDI_READ(hfdi, hf, buf, cfhead_SIZEOF) != cfhead_SIZEOF) { + if (pmii) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_NOT_A_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + } + return FALSE; + } + + /* check basic MSCF signature */ + if (EndGetI32(buf+cfhead_Signature) != 0x4643534d) { + if (pmii) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_NOT_A_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + } + return FALSE; + } + + /* get the number of folders */ + num_folders = EndGetI16(buf+cfhead_NumFolders); + if (num_folders == 0) { + /* PONDERME: is this really invalid? */ + WARN("weird cabinet detect failure: no folders in cabinet\n"); + if (pmii) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_NOT_A_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + } + return FALSE; + } + + /* get the number of files */ + num_files = EndGetI16(buf+cfhead_NumFiles); + if (num_files == 0) { + /* PONDERME: is this really invalid? */ + WARN("weird cabinet detect failure: no files in cabinet\n"); + if (pmii) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_NOT_A_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + } + return FALSE; + } + + /* setid */ + setid = EndGetI16(buf+cfhead_SetID); + + /* cabinet (set) index */ + cabidx = EndGetI16(buf+cfhead_CabinetIndex); + + /* check the header revision */ + if ((buf[cfhead_MajorVersion] > 1) || + (buf[cfhead_MajorVersion] == 1 && buf[cfhead_MinorVersion] > 3)) + { + WARN("cabinet format version > 1.3\n"); + if (pmii) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_UNKNOWN_CABINET_VERSION; + PFDI_INT(hfdi)->perf->erfType = 0; /* ? */ + PFDI_INT(hfdi)->perf->fError = TRUE; + } + return FALSE; + } + + /* pull the flags out */ + flags = EndGetI16(buf+cfhead_Flags); + + /* read the reserved-sizes part of header, if present */ + if (flags & cfheadRESERVE_PRESENT) { + if (PFDI_READ(hfdi, hf, buf, cfheadext_SIZEOF) != cfheadext_SIZEOF) { + ERR("bunk reserve-sizes?\n"); + if (pmii) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; /* ? */ + PFDI_INT(hfdi)->perf->fError = TRUE; + } + return FALSE; + } + + header_resv = EndGetI16(buf+cfheadext_HeaderReserved); + if (pmii) pmii->header_resv = header_resv; + folder_resv = buf[cfheadext_FolderReserved]; + if (pmii) pmii->folder_resv = folder_resv; + block_resv = buf[cfheadext_DataReserved]; + if (pmii) pmii->block_resv = block_resv; + + if (header_resv > 60000) { + WARN("WARNING; header reserved space > 60000\n"); + } + + /* skip the reserved header */ + if ((header_resv) && (PFDI_SEEK(hfdi, hf, header_resv, SEEK_CUR) == -1)) { + ERR("seek failure: header_resv\n"); + if (pmii) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; /* ? */ + PFDI_INT(hfdi)->perf->fError = TRUE; + } + return FALSE; + } + } + + if (flags & cfheadPREV_CABINET) { + prevname = FDI_read_string(hfdi, hf, cabsize); + if (!prevname) { + if (pmii) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; /* ? */ + PFDI_INT(hfdi)->perf->fError = TRUE; + } + return FALSE; + } else + if (pmii) + pmii->prevname = prevname; + else + PFDI_FREE(hfdi, prevname); + previnfo = FDI_read_string(hfdi, hf, cabsize); + if (previnfo) { + if (pmii) + pmii->previnfo = previnfo; + else + PFDI_FREE(hfdi, previnfo); + } + } + + if (flags & cfheadNEXT_CABINET) { + if (pmii) + pmii->hasnext = TRUE; + nextname = FDI_read_string(hfdi, hf, cabsize); + if (!nextname) { + if ((flags & cfheadPREV_CABINET) && pmii) { + if (pmii->prevname) PFDI_FREE(hfdi, prevname); + if (pmii->previnfo) PFDI_FREE(hfdi, previnfo); + } + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; /* ? */ + PFDI_INT(hfdi)->perf->fError = TRUE; + return FALSE; + } else + if (pmii) + pmii->nextname = nextname; + else + PFDI_FREE(hfdi, nextname); + nextinfo = FDI_read_string(hfdi, hf, cabsize); + if (nextinfo) { + if (pmii) + pmii->nextinfo = nextinfo; + else + PFDI_FREE(hfdi, nextinfo); + } + } + + /* we could process the whole cabinet searching for problems; + instead lets stop here. Now let's fill out the paperwork */ + pfdici->cbCabinet = cabsize; + pfdici->cFolders = num_folders; + pfdici->cFiles = num_files; + pfdici->setID = setid; + pfdici->iCabinet = cabidx; + pfdici->fReserve = (flags & cfheadRESERVE_PRESENT) ? TRUE : FALSE; + pfdici->hasprev = (flags & cfheadPREV_CABINET) ? TRUE : FALSE; + pfdici->hasnext = (flags & cfheadNEXT_CABINET) ? TRUE : FALSE; + return TRUE; +} + +/*********************************************************************** + * FDIIsCabinet (CABINET.21) + * + * Informs the caller as to whether or not the provided file handle is + * really a cabinet or not, filling out the provided PFDICABINETINFO + * structure with information about the cabinet. Brief explanations of + * the elements of this structure are available as comments accompanying + * its definition in wine's include/fdi.h. + * + * PARAMS + * hfdi [I] An HFDI from FDICreate + * hf [I] The file handle about which the caller inquires + * pfdici [IO] Pointer to a PFDICABINETINFO structure which will + * be filled out with information about the cabinet + * file indicated by hf if, indeed, it is determined + * to be a cabinet. + * + * RETURNS + * TRUE if the file is a cabinet. The info pointed to by pfdici will + * be provided. + * FALSE if the file is not a cabinet, or if an error was encountered + * while processing the cabinet. The PERF structure provided to + * FDICreate can be queried for more error information. + * + * INCLUDES + * fdi.c + */ +BOOL __cdecl FDIIsCabinet( + HFDI hfdi, + INT_PTR hf, + PFDICABINETINFO pfdici) +{ + BOOL rv; + + TRACE("(hfdi == ^%p, hf == ^%d, pfdici == ^%p)\n", hfdi, hf, pfdici); + + if (!REALLY_IS_FDI(hfdi)) { + ERR("REALLY_IS_FDI failed on ^%p\n", hfdi); + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + if (!hf) { + ERR("(!hf)!\n"); + /* PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CABINET_NOT_FOUND; + PFDI_INT(hfdi)->perf->erfType = ERROR_INVALID_HANDLE; + PFDI_INT(hfdi)->perf->fError = TRUE; */ + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + if (!pfdici) { + ERR("(!pfdici)!\n"); + /* PFDI_INT(hfdi)->perf->erfOper = FDIERROR_NONE; + PFDI_INT(hfdi)->perf->erfType = ERROR_BAD_ARGUMENTS; + PFDI_INT(hfdi)->perf->fError = TRUE; */ + SetLastError(ERROR_BAD_ARGUMENTS); + return FALSE; + } + rv = FDI_read_entries(hfdi, hf, pfdici, NULL); + + if (rv) + pfdici->hasnext = FALSE; /* yuck. duplicate apparent cabinet.dll bug */ + + return rv; +} + +/****************************************************************** + * QTMfdi_initmodel (internal) + * + * Initialize a model which decodes symbols from [s] to [s]+[n]-1 + */ +void QTMfdi_initmodel(struct QTMmodel *m, struct QTMmodelsym *sym, int n, int s) { + int i; + m->shiftsleft = 4; + m->entries = n; + m->syms = sym; + memset(m->tabloc, 0xFF, sizeof(m->tabloc)); /* clear out look-up table */ + for (i = 0; i < n; i++) { + m->tabloc[i+s] = i; /* set up a look-up entry for symbol */ + m->syms[i].sym = i+s; /* actual symbol */ + m->syms[i].cumfreq = n-i; /* current frequency of that symbol */ + } + m->syms[n].cumfreq = 0; +} + +/****************************************************************** + * QTMfdi_init (internal) + */ +int QTMfdi_init(int window, int level, fdi_decomp_state *decomp_state) { + unsigned int wndsize = 1 << window; + int msz = window * 2, i; + cab_ULONG j; + + /* QTM supports window sizes of 2^10 (1Kb) through 2^21 (2Mb) */ + /* if a previously allocated window is big enough, keep it */ + if (window < 10 || window > 21) return DECR_DATAFORMAT; + if (QTM(actual_size) < wndsize) { + if (QTM(window)) PFDI_FREE(CAB(hfdi), QTM(window)); + QTM(window) = NULL; + } + if (!QTM(window)) { + if (!(QTM(window) = PFDI_ALLOC(CAB(hfdi), wndsize))) return DECR_NOMEMORY; + QTM(actual_size) = wndsize; + } + QTM(window_size) = wndsize; + QTM(window_posn) = 0; + + /* initialize static slot/extrabits tables */ + for (i = 0, j = 0; i < 27; i++) { + CAB(q_length_extra)[i] = (i == 26) ? 0 : (i < 2 ? 0 : i - 2) >> 2; + CAB(q_length_base)[i] = j; j += 1 << ((i == 26) ? 5 : CAB(q_length_extra)[i]); + } + for (i = 0, j = 0; i < 42; i++) { + CAB(q_extra_bits)[i] = (i < 2 ? 0 : i-2) >> 1; + CAB(q_position_base)[i] = j; j += 1 << CAB(q_extra_bits)[i]; + } + + /* initialize arithmetic coding models */ + + QTMfdi_initmodel(&QTM(model7), &QTM(m7sym)[0], 7, 0); + + QTMfdi_initmodel(&QTM(model00), &QTM(m00sym)[0], 0x40, 0x00); + QTMfdi_initmodel(&QTM(model40), &QTM(m40sym)[0], 0x40, 0x40); + QTMfdi_initmodel(&QTM(model80), &QTM(m80sym)[0], 0x40, 0x80); + QTMfdi_initmodel(&QTM(modelC0), &QTM(mC0sym)[0], 0x40, 0xC0); + + /* model 4 depends on table size, ranges from 20 to 24 */ + QTMfdi_initmodel(&QTM(model4), &QTM(m4sym)[0], (msz < 24) ? msz : 24, 0); + /* model 5 depends on table size, ranges from 20 to 36 */ + QTMfdi_initmodel(&QTM(model5), &QTM(m5sym)[0], (msz < 36) ? msz : 36, 0); + /* model 6pos depends on table size, ranges from 20 to 42 */ + QTMfdi_initmodel(&QTM(model6pos), &QTM(m6psym)[0], msz, 0); + QTMfdi_initmodel(&QTM(model6len), &QTM(m6lsym)[0], 27, 0); + + return DECR_OK; +} + +/************************************************************ + * LZXfdi_init (internal) + */ +int LZXfdi_init(int window, fdi_decomp_state *decomp_state) { + cab_ULONG wndsize = 1 << window; + int i, j, posn_slots; + + /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */ + /* if a previously allocated window is big enough, keep it */ + if (window < 15 || window > 21) return DECR_DATAFORMAT; + if (LZX(actual_size) < wndsize) { + if (LZX(window)) PFDI_FREE(CAB(hfdi), LZX(window)); + LZX(window) = NULL; + } + if (!LZX(window)) { + if (!(LZX(window) = PFDI_ALLOC(CAB(hfdi), wndsize))) return DECR_NOMEMORY; + LZX(actual_size) = wndsize; + } + LZX(window_size) = wndsize; + + /* initialize static tables */ + for (i=0, j=0; i <= 50; i += 2) { + CAB(extra_bits)[i] = CAB(extra_bits)[i+1] = j; /* 0,0,0,0,1,1,2,2,3,3... */ + if ((i != 0) && (j < 17)) j++; /* 0,0,1,2,3,4...15,16,17,17,17,17... */ + } + for (i=0, j=0; i <= 50; i++) { + CAB(lzx_position_base)[i] = j; /* 0,1,2,3,4,6,8,12,16,24,32,... */ + j += 1 << CAB(extra_bits)[i]; /* 1,1,1,1,2,2,4,4,8,8,16,16,32,32,... */ + } + + /* calculate required position slots */ + if (window == 20) posn_slots = 42; + else if (window == 21) posn_slots = 50; + else posn_slots = window << 1; + + /*posn_slots=i=0; while (i < wndsize) i += 1 << CAB(extra_bits)[posn_slots++]; */ + + LZX(R0) = LZX(R1) = LZX(R2) = 1; + LZX(main_elements) = LZX_NUM_CHARS + (posn_slots << 3); + LZX(header_read) = 0; + LZX(frames_read) = 0; + LZX(block_remaining) = 0; + LZX(block_type) = LZX_BLOCKTYPE_INVALID; + LZX(intel_curpos) = 0; + LZX(intel_started) = 0; + LZX(window_posn) = 0; + + /* initialize tables to 0 (because deltas will be applied to them) */ + for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) LZX(MAINTREE_len)[i] = 0; + for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) LZX(LENGTH_len)[i] = 0; + + return DECR_OK; +} + +/**************************************************** + * NONEfdi_decomp(internal) + */ +int NONEfdi_decomp(int inlen, int outlen, fdi_decomp_state *decomp_state) +{ + if (inlen != outlen) return DECR_ILLEGALDATA; + memcpy(CAB(outbuf), CAB(inbuf), (size_t) inlen); + return DECR_OK; +} + +/******************************************************** + * Ziphuft_free (internal) + */ +void fdi_Ziphuft_free(HFDI hfdi, struct Ziphuft *t) +{ + register struct Ziphuft *p, *q; + + /* Go through linked list, freeing from the allocated (t[-1]) address. */ + p = t; + while (p != (struct Ziphuft *)NULL) + { + q = (--p)->v.t; + PFDI_FREE(hfdi, p); + p = q; + } +} + +/********************************************************* + * fdi_Ziphuft_build (internal) + */ +cab_LONG fdi_Ziphuft_build(cab_ULONG *b, cab_ULONG n, cab_ULONG s, cab_UWORD *d, cab_UWORD *e, +struct Ziphuft **t, cab_LONG *m, fdi_decomp_state *decomp_state) +{ + cab_ULONG a; /* counter for codes of length k */ + cab_ULONG el; /* length of EOB code (value 256) */ + cab_ULONG f; /* i repeats in table every f entries */ + cab_LONG g; /* maximum code length */ + cab_LONG h; /* table level */ + register cab_ULONG i; /* counter, current code */ + register cab_ULONG j; /* counter */ + register cab_LONG k; /* number of bits in current code */ + cab_LONG *l; /* stack of bits per table */ + register cab_ULONG *p; /* pointer into ZIP(c)[],ZIP(b)[],ZIP(v)[] */ + register struct Ziphuft *q; /* points to current table */ + struct Ziphuft r; /* table entry for structure assignment */ + register cab_LONG w; /* bits before this table == (l * h) */ + cab_ULONG *xp; /* pointer into x */ + cab_LONG y; /* number of dummy codes added */ + cab_ULONG z; /* number of entries in current table */ + + l = ZIP(lx)+1; + + /* Generate counts for each bit length */ + el = n > 256 ? b[256] : ZIPBMAX; /* set length of EOB code, if any */ + + for(i = 0; i < ZIPBMAX+1; ++i) + ZIP(c)[i] = 0; + p = b; i = n; + do + { + ZIP(c)[*p]++; p++; /* assume all entries <= ZIPBMAX */ + } while (--i); + if (ZIP(c)[0] == n) /* null input--all zero length codes */ + { + *t = (struct Ziphuft *)NULL; + *m = 0; + return 0; + } + + /* Find minimum and maximum length, bound *m by those */ + for (j = 1; j <= ZIPBMAX; j++) + if (ZIP(c)[j]) + break; + k = j; /* minimum code length */ + if ((cab_ULONG)*m < j) + *m = j; + for (i = ZIPBMAX; i; i--) + if (ZIP(c)[i]) + break; + g = i; /* maximum code length */ + if ((cab_ULONG)*m > i) + *m = i; + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= ZIP(c)[j]) < 0) + return 2; /* bad input: more codes than bits */ + if ((y -= ZIP(c)[i]) < 0) + return 2; + ZIP(c)[i] += y; + + /* Generate starting offsets LONGo the value table for each length */ + ZIP(x)[1] = j = 0; + p = ZIP(c) + 1; xp = ZIP(x) + 2; + while (--i) + { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do{ + if ((j = *p++) != 0) + ZIP(v)[ZIP(x)[j]++] = i; + } while (++i < n); + + + /* Generate the Huffman codes and for each, make the table entries */ + ZIP(x)[0] = i = 0; /* first Huffman code is zero */ + p = ZIP(v); /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = l[-1] = 0; /* no bits decoded yet */ + ZIP(u)[0] = (struct Ziphuft *)NULL; /* just to keep compilers happy */ + q = (struct Ziphuft *)NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = ZIP(c)[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l[h]) + { + w += l[h++]; /* add bits already decoded */ + + /* compute minimum size table less than or equal to *m bits */ + z = (z = g - w) > (cab_ULONG)*m ? *m : z; /* upper limit */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = ZIP(c) + k; + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + if ((cab_ULONG)w + j > el && (cab_ULONG)w < el) + j = el - w; /* make EOB code end at table */ + z = 1 << j; /* table entries for j-bit table */ + l[h] = j; /* set table size in stack */ + + /* allocate and link in new table */ + if (!(q = (struct Ziphuft *) PFDI_ALLOC(CAB(hfdi), (z + 1)*sizeof(struct Ziphuft)))) + { + if(h) + fdi_Ziphuft_free(CAB(hfdi), ZIP(u)[0]); + return 3; /* not enough memory */ + } + *t = q + 1; /* link to list for Ziphuft_free() */ + *(t = &(q->v.t)) = (struct Ziphuft *)NULL; + ZIP(u)[h] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (h) + { + ZIP(x)[h] = i; /* save pattern for backing up */ + r.b = (cab_UBYTE)l[h-1]; /* bits to dump before this table */ + r.e = (cab_UBYTE)(16 + j); /* bits in this table */ + r.v.t = q; /* pointer to this table */ + j = (i & ((1 << w) - 1)) >> (w - l[h-1]); + ZIP(u)[h-1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.b = (cab_UBYTE)(k - w); + if (p >= ZIP(v) + n) + r.e = 99; /* out of values--invalid code */ + else if (*p < s) + { + r.e = (cab_UBYTE)(*p < 256 ? 16 : 15); /* 256 is end-of-block code */ + r.v.n = *p++; /* simple code is just the value */ + } + else + { + r.e = (cab_UBYTE)e[*p - s]; /* non-simple--look up in lists */ + r.v.n = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != ZIP(x)[h]) + w -= l[--h]; /* don't need to update q */ + } + } + + /* return actual size of base table */ + *m = l[0]; + + /* Return true (1) if we were given an incomplete table */ + return y != 0 && g != 1; +} + +/********************************************************* + * fdi_Zipinflate_codes (internal) + */ +cab_LONG fdi_Zipinflate_codes(struct Ziphuft *tl, struct Ziphuft *td, + cab_LONG bl, cab_LONG bd, fdi_decomp_state *decomp_state) +{ + register cab_ULONG e; /* table entry flag/number of extra bits */ + cab_ULONG n, d; /* length and index for copy */ + cab_ULONG w; /* current window position */ + struct Ziphuft *t; /* pointer to table entry */ + cab_ULONG ml, md; /* masks for bl and bd bits */ + register cab_ULONG b; /* bit buffer */ + register cab_ULONG k; /* number of bits in bit buffer */ + + /* make local copies of globals */ + b = ZIP(bb); /* initialize bit buffer */ + k = ZIP(bk); + w = ZIP(window_posn); /* initialize window position */ + + /* inflate the coded data */ + ml = Zipmask[bl]; /* precompute masks for speed */ + md = Zipmask[bd]; + + for(;;) + { + ZIPNEEDBITS((cab_ULONG)bl) + if((e = (t = tl + ((cab_ULONG)b & ml))->e) > 16) + do + { + if (e == 99) + return 1; + ZIPDUMPBITS(t->b) + e -= 16; + ZIPNEEDBITS(e) + } while ((e = (t = t->v.t + ((cab_ULONG)b & Zipmask[e]))->e) > 16); + ZIPDUMPBITS(t->b) + if (e == 16) /* then it's a literal */ + CAB(outbuf)[w++] = (cab_UBYTE)t->v.n; + else /* it's an EOB or a length */ + { + /* exit if end of block */ + if(e == 15) + break; + + /* get length of block to copy */ + ZIPNEEDBITS(e) + n = t->v.n + ((cab_ULONG)b & Zipmask[e]); + ZIPDUMPBITS(e); + + /* decode distance of block to copy */ + ZIPNEEDBITS((cab_ULONG)bd) + if ((e = (t = td + ((cab_ULONG)b & md))->e) > 16) + do { + if (e == 99) + return 1; + ZIPDUMPBITS(t->b) + e -= 16; + ZIPNEEDBITS(e) + } while ((e = (t = t->v.t + ((cab_ULONG)b & Zipmask[e]))->e) > 16); + ZIPDUMPBITS(t->b) + ZIPNEEDBITS(e) + d = w - t->v.n - ((cab_ULONG)b & Zipmask[e]); + ZIPDUMPBITS(e) + do + { + n -= (e = (e = ZIPWSIZE - ((d &= ZIPWSIZE-1) > w ? d : w)) > n ?n:e); + do + { + CAB(outbuf)[w++] = CAB(outbuf)[d++]; + } while (--e); + } while (n); + } + } + + /* restore the globals from the locals */ + ZIP(window_posn) = w; /* restore global window pointer */ + ZIP(bb) = b; /* restore global bit buffer */ + ZIP(bk) = k; + + /* done */ + return 0; +} + +/*********************************************************** + * Zipinflate_stored (internal) + */ +cab_LONG fdi_Zipinflate_stored(fdi_decomp_state *decomp_state) +/* "decompress" an inflated type 0 (stored) block. */ +{ + cab_ULONG n; /* number of bytes in block */ + cab_ULONG w; /* current window position */ + register cab_ULONG b; /* bit buffer */ + register cab_ULONG k; /* number of bits in bit buffer */ + + /* make local copies of globals */ + b = ZIP(bb); /* initialize bit buffer */ + k = ZIP(bk); + w = ZIP(window_posn); /* initialize window position */ + + /* go to byte boundary */ + n = k & 7; + ZIPDUMPBITS(n); + + /* get the length and its complement */ + ZIPNEEDBITS(16) + n = ((cab_ULONG)b & 0xffff); + ZIPDUMPBITS(16) + ZIPNEEDBITS(16) + if (n != (cab_ULONG)((~b) & 0xffff)) + return 1; /* error in compressed data */ + ZIPDUMPBITS(16) + + /* read and output the compressed data */ + while(n--) + { + ZIPNEEDBITS(8) + CAB(outbuf)[w++] = (cab_UBYTE)b; + ZIPDUMPBITS(8) + } + + /* restore the globals from the locals */ + ZIP(window_posn) = w; /* restore global window pointer */ + ZIP(bb) = b; /* restore global bit buffer */ + ZIP(bk) = k; + return 0; +} + +/****************************************************** + * fdi_Zipinflate_fixed (internal) + */ +cab_LONG fdi_Zipinflate_fixed(fdi_decomp_state *decomp_state) +{ + struct Ziphuft *fixed_tl; + struct Ziphuft *fixed_td; + cab_LONG fixed_bl, fixed_bd; + cab_LONG i; /* temporary variable */ + cab_ULONG *l; + + l = ZIP(ll); + + /* literal table */ + for(i = 0; i < 144; i++) + l[i] = 8; + for(; i < 256; i++) + l[i] = 9; + for(; i < 280; i++) + l[i] = 7; + for(; i < 288; i++) /* make a complete, but wrong code set */ + l[i] = 8; + fixed_bl = 7; + if((i = fdi_Ziphuft_build(l, 288, 257, (cab_UWORD *) Zipcplens, + (cab_UWORD *) Zipcplext, &fixed_tl, &fixed_bl, decomp_state))) + return i; + + /* distance table */ + for(i = 0; i < 30; i++) /* make an incomplete code set */ + l[i] = 5; + fixed_bd = 5; + if((i = fdi_Ziphuft_build(l, 30, 0, (cab_UWORD *) Zipcpdist, (cab_UWORD *) Zipcpdext, + &fixed_td, &fixed_bd, decomp_state)) > 1) + { + fdi_Ziphuft_free(CAB(hfdi), fixed_tl); + return i; + } + + /* decompress until an end-of-block code */ + i = fdi_Zipinflate_codes(fixed_tl, fixed_td, fixed_bl, fixed_bd, decomp_state); + + fdi_Ziphuft_free(CAB(hfdi), fixed_td); + fdi_Ziphuft_free(CAB(hfdi), fixed_tl); + return i; +} + +/************************************************************** + * fdi_Zipinflate_dynamic (internal) + */ +cab_LONG fdi_Zipinflate_dynamic(fdi_decomp_state *decomp_state) + /* decompress an inflated type 2 (dynamic Huffman codes) block. */ +{ + cab_LONG i; /* temporary variables */ + cab_ULONG j; + cab_ULONG *ll; + cab_ULONG l; /* last length */ + cab_ULONG m; /* mask for bit lengths table */ + cab_ULONG n; /* number of lengths to get */ + struct Ziphuft *tl; /* literal/length code table */ + struct Ziphuft *td; /* distance code table */ + cab_LONG bl; /* lookup bits for tl */ + cab_LONG bd; /* lookup bits for td */ + cab_ULONG nb; /* number of bit length codes */ + cab_ULONG nl; /* number of literal/length codes */ + cab_ULONG nd; /* number of distance codes */ + register cab_ULONG b; /* bit buffer */ + register cab_ULONG k; /* number of bits in bit buffer */ + + /* make local bit buffer */ + b = ZIP(bb); + k = ZIP(bk); + ll = ZIP(ll); + + /* read in table lengths */ + ZIPNEEDBITS(5) + nl = 257 + ((cab_ULONG)b & 0x1f); /* number of literal/length codes */ + ZIPDUMPBITS(5) + ZIPNEEDBITS(5) + nd = 1 + ((cab_ULONG)b & 0x1f); /* number of distance codes */ + ZIPDUMPBITS(5) + ZIPNEEDBITS(4) + nb = 4 + ((cab_ULONG)b & 0xf); /* number of bit length codes */ + ZIPDUMPBITS(4) + if(nl > 288 || nd > 32) + return 1; /* bad lengths */ + + /* read in bit-length-code lengths */ + for(j = 0; j < nb; j++) + { + ZIPNEEDBITS(3) + ll[Zipborder[j]] = (cab_ULONG)b & 7; + ZIPDUMPBITS(3) + } + for(; j < 19; j++) + ll[Zipborder[j]] = 0; + + /* build decoding table for trees--single level, 7 bit lookup */ + bl = 7; + if((i = fdi_Ziphuft_build(ll, 19, 19, NULL, NULL, &tl, &bl, decomp_state)) != 0) + { + if(i == 1) + fdi_Ziphuft_free(CAB(hfdi), tl); + return i; /* incomplete code set */ + } + + /* read in literal and distance code lengths */ + n = nl + nd; + m = Zipmask[bl]; + i = l = 0; + while((cab_ULONG)i < n) + { + ZIPNEEDBITS((cab_ULONG)bl) + j = (td = tl + ((cab_ULONG)b & m))->b; + ZIPDUMPBITS(j) + j = td->v.n; + if (j < 16) /* length of code in bits (0..15) */ + ll[i++] = l = j; /* save last length in l */ + else if (j == 16) /* repeat last length 3 to 6 times */ + { + ZIPNEEDBITS(2) + j = 3 + ((cab_ULONG)b & 3); + ZIPDUMPBITS(2) + if((cab_ULONG)i + j > n) + return 1; + while (j--) + ll[i++] = l; + } + else if (j == 17) /* 3 to 10 zero length codes */ + { + ZIPNEEDBITS(3) + j = 3 + ((cab_ULONG)b & 7); + ZIPDUMPBITS(3) + if ((cab_ULONG)i + j > n) + return 1; + while (j--) + ll[i++] = 0; + l = 0; + } + else /* j == 18: 11 to 138 zero length codes */ + { + ZIPNEEDBITS(7) + j = 11 + ((cab_ULONG)b & 0x7f); + ZIPDUMPBITS(7) + if ((cab_ULONG)i + j > n) + return 1; + while (j--) + ll[i++] = 0; + l = 0; + } + } + + /* free decoding table for trees */ + fdi_Ziphuft_free(CAB(hfdi), tl); + + /* restore the global bit buffer */ + ZIP(bb) = b; + ZIP(bk) = k; + + /* build the decoding tables for literal/length and distance codes */ + bl = ZIPLBITS; + if((i = fdi_Ziphuft_build(ll, nl, 257, (cab_UWORD *) Zipcplens, (cab_UWORD *) Zipcplext, + &tl, &bl, decomp_state)) != 0) + { + if(i == 1) + fdi_Ziphuft_free(CAB(hfdi), tl); + return i; /* incomplete code set */ + } + bd = ZIPDBITS; + fdi_Ziphuft_build(ll + nl, nd, 0, (cab_UWORD *) Zipcpdist, (cab_UWORD *) Zipcpdext, + &td, &bd, decomp_state); + + /* decompress until an end-of-block code */ + if(fdi_Zipinflate_codes(tl, td, bl, bd, decomp_state)) + return 1; + + /* free the decoding tables, return */ + fdi_Ziphuft_free(CAB(hfdi), tl); + fdi_Ziphuft_free(CAB(hfdi), td); + return 0; +} + +/***************************************************** + * fdi_Zipinflate_block (internal) + */ +cab_LONG fdi_Zipinflate_block(cab_LONG *e, fdi_decomp_state *decomp_state) /* e == last block flag */ +{ /* decompress an inflated block */ + cab_ULONG t; /* block type */ + register cab_ULONG b; /* bit buffer */ + register cab_ULONG k; /* number of bits in bit buffer */ + + /* make local bit buffer */ + b = ZIP(bb); + k = ZIP(bk); + + /* read in last block bit */ + ZIPNEEDBITS(1) + *e = (cab_LONG)b & 1; + ZIPDUMPBITS(1) + + /* read in block type */ + ZIPNEEDBITS(2) + t = (cab_ULONG)b & 3; + ZIPDUMPBITS(2) + + /* restore the global bit buffer */ + ZIP(bb) = b; + ZIP(bk) = k; + + /* inflate that block type */ + if(t == 2) + return fdi_Zipinflate_dynamic(decomp_state); + if(t == 0) + return fdi_Zipinflate_stored(decomp_state); + if(t == 1) + return fdi_Zipinflate_fixed(decomp_state); + /* bad block type */ + return 2; +} + +/**************************************************** + * ZIPfdi_decomp(internal) + */ +int ZIPfdi_decomp(int inlen, int outlen, fdi_decomp_state *decomp_state) +{ + cab_LONG e; /* last block flag */ + + TRACE("(inlen == %d, outlen == %d)\n", inlen, outlen); + + ZIP(inpos) = CAB(inbuf); + ZIP(bb) = ZIP(bk) = ZIP(window_posn) = 0; + if(outlen > ZIPWSIZE) + return DECR_DATAFORMAT; + + /* CK = Chris Kirmse, official Microsoft purloiner */ + if(ZIP(inpos)[0] != 0x43 || ZIP(inpos)[1] != 0x4B) + return DECR_ILLEGALDATA; + ZIP(inpos) += 2; + + do { + if(fdi_Zipinflate_block(&e, decomp_state)) + return DECR_ILLEGALDATA; + } while(!e); + + /* return success */ + return DECR_OK; +} + +/******************************************************************* + * QTMfdi_decomp(internal) + */ +int QTMfdi_decomp(int inlen, int outlen, fdi_decomp_state *decomp_state) +{ + cab_UBYTE *inpos = CAB(inbuf); + cab_UBYTE *window = QTM(window); + cab_UBYTE *runsrc, *rundest; + + cab_ULONG window_posn = QTM(window_posn); + cab_ULONG window_size = QTM(window_size); + + /* used by bitstream macros */ + register int bitsleft, bitrun, bitsneed; + register cab_ULONG bitbuf; + + /* used by GET_SYMBOL */ + cab_ULONG range; + cab_UWORD symf; + int i; + + int extra, togo = outlen, match_length = 0, copy_length; + cab_UBYTE selector, sym; + cab_ULONG match_offset = 0; + + cab_UWORD H = 0xFFFF, L = 0, C; + + TRACE("(inlen == %d, outlen == %d)\n", inlen, outlen); + + /* read initial value of C */ + Q_INIT_BITSTREAM; + Q_READ_BITS(C, 16); + + /* apply 2^x-1 mask */ + window_posn &= window_size - 1; + /* runs can't straddle the window wraparound */ + if ((window_posn + togo) > window_size) { + TRACE("straddled run\n"); + return DECR_DATAFORMAT; + } + + while (togo > 0) { + GET_SYMBOL(model7, selector); + switch (selector) { + case 0: + GET_SYMBOL(model00, sym); window[window_posn++] = sym; togo--; + break; + case 1: + GET_SYMBOL(model40, sym); window[window_posn++] = sym; togo--; + break; + case 2: + GET_SYMBOL(model80, sym); window[window_posn++] = sym; togo--; + break; + case 3: + GET_SYMBOL(modelC0, sym); window[window_posn++] = sym; togo--; + break; + + case 4: + /* selector 4 = fixed length of 3 */ + GET_SYMBOL(model4, sym); + Q_READ_BITS(extra, CAB(q_extra_bits)[sym]); + match_offset = CAB(q_position_base)[sym] + extra + 1; + match_length = 3; + break; + + case 5: + /* selector 5 = fixed length of 4 */ + GET_SYMBOL(model5, sym); + Q_READ_BITS(extra, CAB(q_extra_bits)[sym]); + match_offset = CAB(q_position_base)[sym] + extra + 1; + match_length = 4; + break; + + case 6: + /* selector 6 = variable length */ + GET_SYMBOL(model6len, sym); + Q_READ_BITS(extra, CAB(q_length_extra)[sym]); + match_length = CAB(q_length_base)[sym] + extra + 5; + GET_SYMBOL(model6pos, sym); + Q_READ_BITS(extra, CAB(q_extra_bits)[sym]); + match_offset = CAB(q_position_base)[sym] + extra + 1; + break; + + default: + TRACE("Selector is bogus\n"); + return DECR_ILLEGALDATA; + } + + /* if this is a match */ + if (selector >= 4) { + rundest = window + window_posn; + togo -= match_length; + + /* copy any wrapped around source data */ + if (window_posn >= match_offset) { + /* no wrap */ + runsrc = rundest - match_offset; + } else { + runsrc = rundest + (window_size - match_offset); + copy_length = match_offset - window_posn; + if (copy_length < match_length) { + match_length -= copy_length; + window_posn += copy_length; + while (copy_length-- > 0) *rundest++ = *runsrc++; + runsrc = window; + } + } + window_posn += match_length; + + /* copy match data - no worries about destination wraps */ + while (match_length-- > 0) *rundest++ = *runsrc++; + } + } /* while (togo > 0) */ + + if (togo != 0) { + TRACE("Frame overflow, this_run = %d\n", togo); + return DECR_ILLEGALDATA; + } + + memcpy(CAB(outbuf), window + ((!window_posn) ? window_size : window_posn) - + outlen, outlen); + + QTM(window_posn) = window_posn; + return DECR_OK; +} + +/************************************************************ + * fdi_lzx_read_lens (internal) + */ +int fdi_lzx_read_lens(cab_UBYTE *lens, cab_ULONG first, cab_ULONG last, struct lzx_bits *lb, + fdi_decomp_state *decomp_state) { + cab_ULONG i,j, x,y; + int z; + + register cab_ULONG bitbuf = lb->bb; + register int bitsleft = lb->bl; + cab_UBYTE *inpos = lb->ip; + cab_UWORD *hufftbl; + + for (x = 0; x < 20; x++) { + READ_BITS(y, 4); + LENTABLE(PRETREE)[x] = y; + } + BUILD_TABLE(PRETREE); + + for (x = first; x < last; ) { + READ_HUFFSYM(PRETREE, z); + if (z == 17) { + READ_BITS(y, 4); y += 4; + while (y--) lens[x++] = 0; + } + else if (z == 18) { + READ_BITS(y, 5); y += 20; + while (y--) lens[x++] = 0; + } + else if (z == 19) { + READ_BITS(y, 1); y += 4; + READ_HUFFSYM(PRETREE, z); + z = lens[x] - z; if (z < 0) z += 17; + while (y--) lens[x++] = z; + } + else { + z = lens[x] - z; if (z < 0) z += 17; + lens[x++] = z; + } + } + + lb->bb = bitbuf; + lb->bl = bitsleft; + lb->ip = inpos; + return 0; +} + +/******************************************************* + * LZXfdi_decomp(internal) + */ +int LZXfdi_decomp(int inlen, int outlen, fdi_decomp_state *decomp_state) { + cab_UBYTE *inpos = CAB(inbuf); + cab_UBYTE *endinp = inpos + inlen; + cab_UBYTE *window = LZX(window); + cab_UBYTE *runsrc, *rundest; + cab_UWORD *hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */ + + cab_ULONG window_posn = LZX(window_posn); + cab_ULONG window_size = LZX(window_size); + cab_ULONG R0 = LZX(R0); + cab_ULONG R1 = LZX(R1); + cab_ULONG R2 = LZX(R2); + + register cab_ULONG bitbuf; + register int bitsleft; + cab_ULONG match_offset, i,j,k; /* ijk used in READ_HUFFSYM macro */ + struct lzx_bits lb; /* used in READ_LENGTHS macro */ + + int togo = outlen, this_run, main_element, aligned_bits; + int match_length, copy_length, length_footer, extra, verbatim_bits; + + TRACE("(inlen == %d, outlen == %d)\n", inlen, outlen); + + INIT_BITSTREAM; + + /* read header if necessary */ + if (!LZX(header_read)) { + i = j = 0; + READ_BITS(k, 1); if (k) { READ_BITS(i,16); READ_BITS(j,16); } + LZX(intel_filesize) = (i << 16) | j; /* or 0 if not encoded */ + LZX(header_read) = 1; + } + + /* main decoding loop */ + while (togo > 0) { + /* last block finished, new block expected */ + if (LZX(block_remaining) == 0) { + if (LZX(block_type) == LZX_BLOCKTYPE_UNCOMPRESSED) { + if (LZX(block_length) & 1) inpos++; /* realign bitstream to word */ + INIT_BITSTREAM; + } + + READ_BITS(LZX(block_type), 3); + READ_BITS(i, 16); + READ_BITS(j, 8); + LZX(block_remaining) = LZX(block_length) = (i << 8) | j; + + switch (LZX(block_type)) { + case LZX_BLOCKTYPE_ALIGNED: + for (i = 0; i < 8; i++) { READ_BITS(j, 3); LENTABLE(ALIGNED)[i] = j; } + BUILD_TABLE(ALIGNED); + /* rest of aligned header is same as verbatim */ + + case LZX_BLOCKTYPE_VERBATIM: + READ_LENGTHS(MAINTREE, 0, 256, fdi_lzx_read_lens); + READ_LENGTHS(MAINTREE, 256, LZX(main_elements), fdi_lzx_read_lens); + BUILD_TABLE(MAINTREE); + if (LENTABLE(MAINTREE)[0xE8] != 0) LZX(intel_started) = 1; + + READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS, fdi_lzx_read_lens); + BUILD_TABLE(LENGTH); + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + LZX(intel_started) = 1; /* because we can't assume otherwise */ + ENSURE_BITS(16); /* get up to 16 pad bits into the buffer */ + if (bitsleft > 16) inpos -= 2; /* and align the bitstream! */ + R0 = inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4; + R1 = inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4; + R2 = inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4; + break; + + default: + return DECR_ILLEGALDATA; + } + } + + /* buffer exhaustion check */ + if (inpos > endinp) { + /* it's possible to have a file where the next run is less than + * 16 bits in size. In this case, the READ_HUFFSYM() macro used + * in building the tables will exhaust the buffer, so we should + * allow for this, but not allow those accidentally read bits to + * be used (so we check that there are at least 16 bits + * remaining - in this boundary case they aren't really part of + * the compressed data) + */ + if (inpos > (endinp+2) || bitsleft < 16) return DECR_ILLEGALDATA; + } + + while ((this_run = LZX(block_remaining)) > 0 && togo > 0) { + if (this_run > togo) this_run = togo; + togo -= this_run; + LZX(block_remaining) -= this_run; + + /* apply 2^x-1 mask */ + window_posn &= window_size - 1; + /* runs can't straddle the window wraparound */ + if ((window_posn + this_run) > window_size) + return DECR_DATAFORMAT; + + switch (LZX(block_type)) { + + case LZX_BLOCKTYPE_VERBATIM: + while (this_run > 0) { + READ_HUFFSYM(MAINTREE, main_element); + + if (main_element < LZX_NUM_CHARS) { + /* literal: 0 to LZX_NUM_CHARS-1 */ + window[window_posn++] = main_element; + this_run--; + } + else { + /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ + main_element -= LZX_NUM_CHARS; + + match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) { + READ_HUFFSYM(LENGTH, length_footer); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + match_offset = main_element >> 3; + + if (match_offset > 2) { + /* not repeated offset */ + if (match_offset != 3) { + extra = CAB(extra_bits)[match_offset]; + READ_BITS(verbatim_bits, extra); + match_offset = CAB(lzx_position_base)[match_offset] + - 2 + verbatim_bits; + } + else { + match_offset = 1; + } + + /* update repeated offset LRU queue */ + R2 = R1; R1 = R0; R0 = match_offset; + } + else if (match_offset == 0) { + match_offset = R0; + } + else if (match_offset == 1) { + match_offset = R1; + R1 = R0; R0 = match_offset; + } + else /* match_offset == 2 */ { + match_offset = R2; + R2 = R0; R0 = match_offset; + } + + rundest = window + window_posn; + this_run -= match_length; + + /* copy any wrapped around source data */ + if (window_posn >= match_offset) { + /* no wrap */ + runsrc = rundest - match_offset; + } else { + runsrc = rundest + (window_size - match_offset); + copy_length = match_offset - window_posn; + if (copy_length < match_length) { + match_length -= copy_length; + window_posn += copy_length; + while (copy_length-- > 0) *rundest++ = *runsrc++; + runsrc = window; + } + } + window_posn += match_length; + + /* copy match data - no worries about destination wraps */ + while (match_length-- > 0) *rundest++ = *runsrc++; + } + } + break; + + case LZX_BLOCKTYPE_ALIGNED: + while (this_run > 0) { + READ_HUFFSYM(MAINTREE, main_element); + + if (main_element < LZX_NUM_CHARS) { + /* literal: 0 to LZX_NUM_CHARS-1 */ + window[window_posn++] = main_element; + this_run--; + } + else { + /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ + main_element -= LZX_NUM_CHARS; + + match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) { + READ_HUFFSYM(LENGTH, length_footer); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + match_offset = main_element >> 3; + + if (match_offset > 2) { + /* not repeated offset */ + extra = CAB(extra_bits)[match_offset]; + match_offset = CAB(lzx_position_base)[match_offset] - 2; + if (extra > 3) { + /* verbatim and aligned bits */ + extra -= 3; + READ_BITS(verbatim_bits, extra); + match_offset += (verbatim_bits << 3); + READ_HUFFSYM(ALIGNED, aligned_bits); + match_offset += aligned_bits; + } + else if (extra == 3) { + /* aligned bits only */ + READ_HUFFSYM(ALIGNED, aligned_bits); + match_offset += aligned_bits; + } + else if (extra > 0) { /* extra==1, extra==2 */ + /* verbatim bits only */ + READ_BITS(verbatim_bits, extra); + match_offset += verbatim_bits; + } + else /* extra == 0 */ { + /* ??? */ + match_offset = 1; + } + + /* update repeated offset LRU queue */ + R2 = R1; R1 = R0; R0 = match_offset; + } + else if (match_offset == 0) { + match_offset = R0; + } + else if (match_offset == 1) { + match_offset = R1; + R1 = R0; R0 = match_offset; + } + else /* match_offset == 2 */ { + match_offset = R2; + R2 = R0; R0 = match_offset; + } + + rundest = window + window_posn; + this_run -= match_length; + + /* copy any wrapped around source data */ + if (window_posn >= match_offset) { + /* no wrap */ + runsrc = rundest - match_offset; + } else { + runsrc = rundest + (window_size - match_offset); + copy_length = match_offset - window_posn; + if (copy_length < match_length) { + match_length -= copy_length; + window_posn += copy_length; + while (copy_length-- > 0) *rundest++ = *runsrc++; + runsrc = window; + } + } + window_posn += match_length; + + /* copy match data - no worries about destination wraps */ + while (match_length-- > 0) *rundest++ = *runsrc++; + } + } + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + if ((inpos + this_run) > endinp) return DECR_ILLEGALDATA; + memcpy(window + window_posn, inpos, (size_t) this_run); + inpos += this_run; window_posn += this_run; + break; + + default: + return DECR_ILLEGALDATA; /* might as well */ + } + + } + } + + if (togo != 0) return DECR_ILLEGALDATA; + memcpy(CAB(outbuf), window + ((!window_posn) ? window_size : window_posn) - + outlen, (size_t) outlen); + + LZX(window_posn) = window_posn; + LZX(R0) = R0; + LZX(R1) = R1; + LZX(R2) = R2; + + /* intel E8 decoding */ + if ((LZX(frames_read)++ < 32768) && LZX(intel_filesize) != 0) { + if (outlen <= 6 || !LZX(intel_started)) { + LZX(intel_curpos) += outlen; + } + else { + cab_UBYTE *data = CAB(outbuf); + cab_UBYTE *dataend = data + outlen - 10; + cab_LONG curpos = LZX(intel_curpos); + cab_LONG filesize = LZX(intel_filesize); + cab_LONG abs_off, rel_off; + + LZX(intel_curpos) = curpos + outlen; + + while (data < dataend) { + if (*data++ != 0xE8) { curpos++; continue; } + abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); + if ((abs_off >= -curpos) && (abs_off < filesize)) { + rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; + data[0] = (cab_UBYTE) rel_off; + data[1] = (cab_UBYTE) (rel_off >> 8); + data[2] = (cab_UBYTE) (rel_off >> 16); + data[3] = (cab_UBYTE) (rel_off >> 24); + } + data += 4; + curpos += 5; + } + } + } + return DECR_OK; +} + +/********************************************************** + * fdi_decomp (internal) + * + * Decompress the requested number of bytes. If savemode is zero, + * do not save the output anywhere, just plow through blocks until we + * reach the specified (uncompressed) distance from the starting point, + * and remember the position of the cabfile pointer (and which cabfile) + * after we are done; otherwise, save the data out to CAB(filehf), + * decompressing the requested number of bytes and writing them out. This + * is also where we jump to additional cabinets in the case of split + * cab's, and provide (some of) the NEXT_CABINET notification semantics. + */ +int fdi_decomp(struct fdi_file *fi, int savemode, fdi_decomp_state *decomp_state, + char *pszCabPath, PFNFDINOTIFY pfnfdin, void *pvUser) +{ + cab_ULONG bytes = savemode ? fi->length : fi->offset - CAB(offset); + cab_UBYTE buf[cfdata_SIZEOF], *data; + cab_UWORD inlen, len, outlen, cando; + cab_ULONG cksum; + cab_LONG err; + fdi_decomp_state *cab = (savemode && CAB(decomp_cab)) ? CAB(decomp_cab) : decomp_state; + + TRACE("(fi == ^%p, savemode == %d, bytes == %d)\n", fi, savemode, bytes); + + while (bytes > 0) { + /* cando = the max number of bytes we can do */ + cando = CAB(outlen); + if (cando > bytes) cando = bytes; + + /* if cando != 0 */ + if (cando && savemode) + PFDI_WRITE(CAB(hfdi), CAB(filehf), CAB(outpos), cando); + + CAB(outpos) += cando; + CAB(outlen) -= cando; + bytes -= cando; if (!bytes) break; + + /* we only get here if we emptied the output buffer */ + + /* read data header + data */ + inlen = outlen = 0; + while (outlen == 0) { + /* read the block header, skip the reserved part */ + if (PFDI_READ(CAB(hfdi), cab->cabhf, buf, cfdata_SIZEOF) != cfdata_SIZEOF) + return DECR_INPUT; + + if (PFDI_SEEK(CAB(hfdi), cab->cabhf, cab->mii.block_resv, SEEK_CUR) == -1) + return DECR_INPUT; + + /* we shouldn't get blocks over CAB_INPUTMAX in size */ + data = CAB(inbuf) + inlen; + len = EndGetI16(buf+cfdata_CompressedSize); + inlen += len; + if (inlen > CAB_INPUTMAX) return DECR_INPUT; + if (PFDI_READ(CAB(hfdi), cab->cabhf, data, len) != len) + return DECR_INPUT; + + /* clear two bytes after read-in data */ + data[len+1] = data[len+2] = 0; + + /* perform checksum test on the block (if one is stored) */ + cksum = EndGetI32(buf+cfdata_CheckSum); + if (cksum && cksum != checksum(buf+4, 4, checksum(data, len, 0))) + return DECR_CHECKSUM; /* checksum is wrong */ + + outlen = EndGetI16(buf+cfdata_UncompressedSize); + + /* outlen=0 means this block was the last contiguous part + of a split block, continued in the next cabinet */ + if (outlen == 0) { + int pathlen, filenamelen, idx, i, cabhf; + char fullpath[MAX_PATH], userpath[256]; + FDINOTIFICATION fdin; + FDICABINETINFO fdici; + char emptystring = '\0'; + cab_UBYTE buf2[64]; + int success = FALSE; + struct fdi_folder *fol = NULL, *linkfol = NULL; + struct fdi_file *file = NULL, *linkfile = NULL; + + tryanothercab: + + /* set up the next decomp_state... */ + if (!(cab->next)) { + if (!cab->mii.hasnext) return DECR_INPUT; + + if (!((cab->next = PFDI_ALLOC(CAB(hfdi), sizeof(fdi_decomp_state))))) + return DECR_NOMEMORY; + + ZeroMemory(cab->next, sizeof(fdi_decomp_state)); + + /* copy pszCabPath to userpath */ + ZeroMemory(userpath, 256); + pathlen = (pszCabPath) ? strlen(pszCabPath) : 0; + if (pathlen) { + if (pathlen < 256) { + for (i = 0; i <= pathlen; i++) + userpath[i] = pszCabPath[i]; + } /* else we are in a weird place... let's leave it blank and see if the user fixes it */ + } + + /* initial fdintNEXT_CABINET notification */ + ZeroMemory(&fdin, sizeof(FDINOTIFICATION)); + fdin.psz1 = (cab->mii.nextname) ? cab->mii.nextname : &emptystring; + fdin.psz2 = (cab->mii.nextinfo) ? cab->mii.nextinfo : &emptystring; + fdin.psz3 = &userpath[0]; + fdin.fdie = FDIERROR_NONE; + fdin.pv = pvUser; + + if (((*pfnfdin)(fdintNEXT_CABINET, &fdin))) return DECR_USERABORT; + + do { + + pathlen = (userpath) ? strlen(userpath) : 0; + filenamelen = (cab->mii.nextname) ? strlen(cab->mii.nextname) : 0; + + /* slight overestimation here to save CPU cycles in the developer's brain */ + if ((pathlen + filenamelen + 3) > MAX_PATH) { + ERR("MAX_PATH exceeded.\n"); + return DECR_ILLEGALDATA; + } + + /* paste the path and filename together */ + idx = 0; + if (pathlen) { + for (i = 0; i < pathlen; i++) fullpath[idx++] = userpath[i]; + if (fullpath[idx - 1] != '\\') fullpath[idx++] = '\\'; + } + if (filenamelen) for (i = 0; i < filenamelen; i++) fullpath[idx++] = cab->mii.nextname[i]; + fullpath[idx] = '\0'; + + TRACE("full cab path/file name: %s\n", debugstr_a(fullpath)); + + /* try to get a handle to the cabfile */ + cabhf = PFDI_OPEN(CAB(hfdi), fullpath, 32768, _S_IREAD | _S_IWRITE); + if (cabhf == -1) { + /* no file. allow the user to try again */ + fdin.fdie = FDIERROR_CABINET_NOT_FOUND; + if (((*pfnfdin)(fdintNEXT_CABINET, &fdin))) return DECR_USERABORT; + continue; + } + + if (cabhf == 0) { + ERR("PFDI_OPEN returned zero for %s.\n", fullpath); + fdin.fdie = FDIERROR_CABINET_NOT_FOUND; + if (((*pfnfdin)(fdintNEXT_CABINET, &fdin))) return DECR_USERABORT; + continue; + } + + /* check if it's really a cabfile. Note that this doesn't implement the bug */ + if (!FDI_read_entries(CAB(hfdi), cabhf, &fdici, &(cab->next->mii))) { + WARN("FDIIsCabinet failed.\n"); + PFDI_CLOSE(CAB(hfdi), cabhf); + fdin.fdie = FDIERROR_NOT_A_CABINET; + if (((*pfnfdin)(fdintNEXT_CABINET, &fdin))) return DECR_USERABORT; + continue; + } + + if ((fdici.setID != cab->setID) || (fdici.iCabinet != (cab->iCabinet + 1))) { + WARN("Wrong Cabinet.\n"); + PFDI_CLOSE(CAB(hfdi), cabhf); + fdin.fdie = FDIERROR_WRONG_CABINET; + if (((*pfnfdin)(fdintNEXT_CABINET, &fdin))) return DECR_USERABORT; + continue; + } + + break; + + } while (1); + + /* cabinet notification */ + ZeroMemory(&fdin, sizeof(FDINOTIFICATION)); + fdin.setID = fdici.setID; + fdin.iCabinet = fdici.iCabinet; + fdin.pv = pvUser; + fdin.psz1 = (cab->next->mii.nextname) ? cab->next->mii.nextname : &emptystring; + fdin.psz2 = (cab->next->mii.nextinfo) ? cab->next->mii.nextinfo : &emptystring; + fdin.psz3 = pszCabPath; + + if (((*pfnfdin)(fdintCABINET_INFO, &fdin))) return DECR_USERABORT; + + cab->next->setID = fdici.setID; + cab->next->iCabinet = fdici.iCabinet; + cab->next->hfdi = CAB(hfdi); + cab->next->filehf = CAB(filehf); + cab->next->cabhf = cabhf; + cab->next->decompress = CAB(decompress); /* crude, but unused anyhow */ + + cab = cab->next; /* advance to the next cabinet */ + + /* read folders */ + for (i = 0; i < fdici.cFolders; i++) { + if (PFDI_READ(CAB(hfdi), cab->cabhf, buf2, cffold_SIZEOF) != cffold_SIZEOF) + return DECR_INPUT; + + if (cab->mii.folder_resv > 0) + PFDI_SEEK(CAB(hfdi), cab->cabhf, cab->mii.folder_resv, SEEK_CUR); + + fol = (struct fdi_folder *) PFDI_ALLOC(CAB(hfdi), sizeof(struct fdi_folder)); + if (!fol) { + ERR("out of memory!\n"); + return DECR_NOMEMORY; + } + ZeroMemory(fol, sizeof(struct fdi_folder)); + if (!(cab->firstfol)) cab->firstfol = fol; + + fol->offset = (cab_off_t) EndGetI32(buf2+cffold_DataOffset); + fol->num_blocks = EndGetI16(buf2+cffold_NumBlocks); + fol->comp_type = EndGetI16(buf2+cffold_CompType); + + if (linkfol) + linkfol->next = fol; + linkfol = fol; + } + + /* read files */ + for (i = 0; i < fdici.cFiles; i++) { + if (PFDI_READ(CAB(hfdi), cab->cabhf, buf2, cffile_SIZEOF) != cffile_SIZEOF) + return DECR_INPUT; + + file = (struct fdi_file *) PFDI_ALLOC(CAB(hfdi), sizeof(struct fdi_file)); + if (!file) { + ERR("out of memory!\n"); + return DECR_NOMEMORY; + } + ZeroMemory(file, sizeof(struct fdi_file)); + if (!(cab->firstfile)) cab->firstfile = file; + + file->length = EndGetI32(buf2+cffile_UncompressedSize); + file->offset = EndGetI32(buf2+cffile_FolderOffset); + file->index = EndGetI16(buf2+cffile_FolderIndex); + file->time = EndGetI16(buf2+cffile_Time); + file->date = EndGetI16(buf2+cffile_Date); + file->attribs = EndGetI16(buf2+cffile_Attribs); + file->filename = FDI_read_string(CAB(hfdi), cab->cabhf, fdici.cbCabinet); + + if (!file->filename) return DECR_INPUT; + + if (linkfile) + linkfile->next = file; + linkfile = file; + } + + } else + cab = cab->next; /* advance to the next cabinet */ + + /* iterate files -- if we encounter the continued file, process it -- + otherwise, jump to the label above and keep looking */ + + for (file = cab->firstfile; (file); file = file->next) { + if ((file->index & cffileCONTINUED_FROM_PREV) == cffileCONTINUED_FROM_PREV) { + /* check to ensure a real match */ + if (strcasecmp(fi->filename, file->filename) == 0) { + success = TRUE; + if (PFDI_SEEK(CAB(hfdi), cab->cabhf, cab->firstfol->offset, SEEK_SET) == -1) + return DECR_INPUT; + break; + } + } + } + if (!success) goto tryanothercab; /* FIXME: shouldn't this trigger + "Wrong Cabinet" notification? */ + } + } + + /* decompress block */ + if ((err = CAB(decompress)(inlen, outlen, decomp_state))) + return err; + CAB(outlen) = outlen; + CAB(outpos) = CAB(outbuf); + } + + CAB(decomp_cab) = cab; + return DECR_OK; +} + +/*********************************************************************** + * FDICopy (CABINET.22) + * + * Iterates through the files in the Cabinet file indicated by name and + * file-location. May chain forward to additional cabinets (typically + * only one) if files which begin in this Cabinet are continued in another + * cabinet. For each file which is partially contained in this cabinet, + * and partially contained in a prior cabinet, provides fdintPARTIAL_FILE + * notification to the pfnfdin callback. For each file which begins in + * this cabinet, fdintCOPY_FILE notification is provided to the pfnfdin + * callback, and the file is optionally decompressed and saved to disk. + * Notification is not provided for files which are not at least partially + * contained in the specified cabinet file. + * + * See below for a thorough explanation of the various notification + * callbacks. + * + * PARAMS + * hfdi [I] An HFDI from FDICreate + * pszCabinet [I] C-style string containing the filename of the cabinet + * pszCabPath [I] C-style string containing the file path of the cabinet + * flags [I] "Decoder parameters". Ignored. Suggested value: 0. + * pfnfdin [I] Pointer to a notification function. See CALLBACKS below. + * pfnfdid [I] Pointer to a decryption function. Ignored. Suggested + * value: NULL. + * pvUser [I] arbitrary void * value which is passed to callbacks. + * + * RETURNS + * TRUE if successful. + * FALSE if unsuccessful (error information is provided in the ERF structure + * associated with the provided decompression handle by FDICreate). + * + * CALLBACKS + * + * Two pointers to callback functions are provided as parameters to FDICopy: + * pfnfdin(of type PFNFDINOTIFY), and pfnfdid (of type PFNFDIDECRYPT). These + * types are as follows: + * + * typedef INT_PTR (__cdecl *PFNFDINOTIFY) ( FDINOTIFICATIONTYPE fdint, + * PFDINOTIFICATION pfdin ); + * + * typedef int (__cdecl *PFNFDIDECRYPT) ( PFDIDECRYPT pfdid ); + * + * You can create functions of this type using the FNFDINOTIFY() and + * FNFDIDECRYPT() macros, respectively. For example: + * + * FNFDINOTIFY(mycallback) { + * / * use variables fdint and pfdin to process notification * / + * } + * + * The second callback, which could be used for decrypting encrypted data, + * is not used at all. + * + * Each notification informs the user of some event which has occurred during + * decompression of the cabinet file; each notification is also an opportunity + * for the callee to abort decompression. The information provided to the + * callback and the meaning of the callback's return value vary drastically + * across the various types of notification. The type of notification is the + * fdint parameter; all other information is provided to the callback in + * notification-specific parts of the FDINOTIFICATION structure pointed to by + * pfdin. The only part of that structure which is assigned for every callback + * is the pv element, which contains the arbitrary value which was passed to + * FDICopy in the pvUser argument (psz1 is also used each time, but its meaning + * is highly dependent on fdint). + * + * If you encounter unknown notifications, you should return zero if you want + * decompression to continue (or -1 to abort). All strings used in the + * callbacks are regular C-style strings. Detailed descriptions of each + * notification type follow: + * + * fdintCABINET_INFO: + * + * This is the first notification provided after calling FDICopy, and provides + * the user with various information about the cabinet. Note that this is + * called for each cabinet FDICopy opens, not just the first one. In the + * structure pointed to by pfdin, psz1 contains a pointer to the name of the + * next cabinet file in the set after the one just loaded (if any), psz2 + * contains a pointer to the name or "info" of the next disk, psz3 + * contains a pointer to the file-path of the current cabinet, setID + * contains an arbitrary constant associated with this set of cabinet files, + * and iCabinet contains the numerical index of the current cabinet within + * that set. Return zero, or -1 to abort. + * + * fdintPARTIAL_FILE: + * + * This notification is provided when FDICopy encounters a part of a file + * contained in this cabinet which is missing its beginning. Files can be + * split across cabinets, so this is not necessarily an abnormality; it just + * means that the file in question begins in another cabinet. No file + * corresponding to this notification is extracted from the cabinet. In the + * structure pointed to by pfdin, psz1 contains a pointer to the name of the + * partial file, psz2 contains a pointer to the file name of the cabinet in + * which this file begins, and psz3 contains a pointer to the disk name or + * "info" of the cabinet where the file begins. Return zero, or -1 to abort. + * + * fdintCOPY_FILE: + * + * This notification is provided when FDICopy encounters a file which starts + * in the cabinet file, provided to FDICopy in pszCabinet. (FDICopy will not + * look for files in cabinets after the first one). One notification will be + * sent for each such file, before the file is decompressed. By returning + * zero, the callback can instruct FDICopy to skip the file. In the structure + * pointed to by pfdin, psz1 contains a pointer to the file's name, cb contains + * the size of the file (uncompressed), attribs contains the file attributes, + * and date and time contain the date and time of the file. attributes, date, + * and time are of the 16-bit ms-dos variety. Return -1 to abort decompression + * for the entire cabinet, 0 to skip just this file but continue scanning the + * cabinet for more files, or an FDIClose()-compatible file-handle. + * + * fdintCLOSE_FILE_INFO: + * + * This notification is important, don't forget to implement it. This + * notification indicates that a file has been successfully uncompressed and + * written to disk. Upon receipt of this notification, the callee is expected + * to close the file handle, to set the attributes and date/time of the + * closed file, and possibly to execute the file. In the structure pointed to + * by pfdin, psz1 contains a pointer to the name of the file, hf will be the + * open file handle (close it), cb contains 1 or zero, indicating respectively + * that the callee should or should not execute the file, and date, time + * and attributes will be set as in fdintCOPY_FILE. Bizarrely, the Cabinet SDK + * specifies that _A_EXEC will be xor'ed out of attributes! wine does not do + * do so. Return TRUE, or FALSE to abort decompression. + * + * fdintNEXT_CABINET: + * + * This notification is called when FDICopy must load in another cabinet. This + * can occur when a file's data is "split" across multiple cabinets. The + * callee has the opportunity to request that FDICopy look in a different file + * path for the specified cabinet file, by writing that data into a provided + * buffer (see below for more information). This notification will be received + * more than once per-cabinet in the instance that FDICopy failed to find a + * valid cabinet at the location specified by the first per-cabinet + * fdintNEXT_CABINET notification. In such instances, the fdie element of the + * structure pointed to by pfdin indicates the error which prevented FDICopy + * from proceeding successfully. Return zero to indicate success, or -1 to + * indicate failure and abort FDICopy. + * + * Upon receipt of this notification, the structure pointed to by pfdin will + * contain the following values: psz1 pointing to the name of the cabinet + * which FDICopy is attempting to open, psz2 pointing to the name ("info") of + * the next disk, psz3 pointing to the presumed file-location of the cabinet, + * and fdie containing either FDIERROR_NONE, or one of the following: + * + * FDIERROR_CABINET_NOT_FOUND, FDIERROR_NOT_A_CABINET, + * FDIERROR_UNKNOWN_CABINET_VERSION, FDIERROR_CORRUPT_CABINET, + * FDIERROR_BAD_COMPR_TYPE, FDIERROR_RESERVE_MISMATCH, and + * FDIERROR_WRONG_CABINET. + * + * The callee may choose to change the path where FDICopy will look for the + * cabinet after this notification. To do so, the caller may write the new + * pathname to the buffer pointed to by psz3, which is 256 characters in + * length, including the terminating null character, before returning zero. + * + * fdintENUMERATE: + * + * Undocumented and unimplemented in wine, this seems to be sent each time + * a cabinet is opened, along with the fdintCABINET_INFO notification. It + * probably has an interface similar to that of fdintCABINET_INFO; maybe this + * provides information about the current cabinet instead of the next one.... + * this is just a guess, it has not been looked at closely. + * + * INCLUDES + * fdi.c + */ +BOOL __cdecl FDICopy( + HFDI hfdi, + char *pszCabinet, + char *pszCabPath, + int flags, + PFNFDINOTIFY pfnfdin, + PFNFDIDECRYPT pfnfdid, + void *pvUser) +{ + FDICABINETINFO fdici; + FDINOTIFICATION fdin; + int cabhf, filehf, idx; + unsigned int i; + char fullpath[MAX_PATH]; + size_t pathlen, filenamelen; + char emptystring = '\0'; + cab_UBYTE buf[64]; + struct fdi_folder *fol = NULL, *linkfol = NULL; + struct fdi_file *file = NULL, *linkfile = NULL; + fdi_decomp_state _decomp_state; + fdi_decomp_state *decomp_state = &_decomp_state; + + TRACE("(hfdi == ^%p, pszCabinet == ^%p, pszCabPath == ^%p, flags == %0d, \ + pfnfdin == ^%p, pfnfdid == ^%p, pvUser == ^%p)\n", + hfdi, pszCabinet, pszCabPath, flags, pfnfdin, pfnfdid, pvUser); + + if (!REALLY_IS_FDI(hfdi)) { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + ZeroMemory(decomp_state, sizeof(fdi_decomp_state)); + + pathlen = (pszCabPath) ? strlen(pszCabPath) : 0; + filenamelen = (pszCabinet) ? strlen(pszCabinet) : 0; + + /* slight overestimation here to save CPU cycles in the developer's brain */ + if ((pathlen + filenamelen + 3) > MAX_PATH) { + ERR("MAX_PATH exceeded.\n"); + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CABINET_NOT_FOUND; + PFDI_INT(hfdi)->perf->erfType = ERROR_FILE_NOT_FOUND; + PFDI_INT(hfdi)->perf->fError = TRUE; + SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } + + /* paste the path and filename together */ + idx = 0; + if (pathlen) { + for (i = 0; i < pathlen; i++) fullpath[idx++] = pszCabPath[i]; + if (fullpath[idx - 1] != '\\') fullpath[idx++] = '\\'; + } + if (filenamelen) for (i = 0; i < filenamelen; i++) fullpath[idx++] = pszCabinet[i]; + fullpath[idx] = '\0'; + + TRACE("full cab path/file name: %s\n", debugstr_a(fullpath)); + + /* get a handle to the cabfile */ + cabhf = PFDI_OPEN(hfdi, fullpath, 32768, _S_IREAD | _S_IWRITE); + if (cabhf == -1) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CABINET_NOT_FOUND; + PFDI_INT(hfdi)->perf->erfType = ERROR_FILE_NOT_FOUND; + PFDI_INT(hfdi)->perf->fError = TRUE; + SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } + + if (cabhf == 0) { + ERR("PFDI_OPEN returned zero for %s.\n", fullpath); + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CABINET_NOT_FOUND; + PFDI_INT(hfdi)->perf->erfType = ERROR_FILE_NOT_FOUND; + PFDI_INT(hfdi)->perf->fError = TRUE; + SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } + + /* check if it's really a cabfile. Note that this doesn't implement the bug */ + if (!FDI_read_entries(hfdi, cabhf, &fdici, &(CAB(mii)))) { + ERR("FDIIsCabinet failed.\n"); + PFDI_CLOSE(hfdi, cabhf); + return FALSE; + } + + /* cabinet notification */ + ZeroMemory(&fdin, sizeof(FDINOTIFICATION)); + fdin.setID = fdici.setID; + fdin.iCabinet = fdici.iCabinet; + fdin.pv = pvUser; + fdin.psz1 = (CAB(mii).nextname) ? CAB(mii).nextname : &emptystring; + fdin.psz2 = (CAB(mii).nextinfo) ? CAB(mii).nextinfo : &emptystring; + fdin.psz3 = pszCabPath; + + if (((*pfnfdin)(fdintCABINET_INFO, &fdin))) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_USER_ABORT; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + goto bail_and_fail; + } + + CAB(setID) = fdici.setID; + CAB(iCabinet) = fdici.iCabinet; + + /* read folders */ + for (i = 0; i < fdici.cFolders; i++) { + if (PFDI_READ(hfdi, cabhf, buf, cffold_SIZEOF) != cffold_SIZEOF) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + goto bail_and_fail; + } + + if (CAB(mii).folder_resv > 0) + PFDI_SEEK(hfdi, cabhf, CAB(mii).folder_resv, SEEK_CUR); + + fol = (struct fdi_folder *) PFDI_ALLOC(hfdi, sizeof(struct fdi_folder)); + if (!fol) { + ERR("out of memory!\n"); + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_ALLOC_FAIL; + PFDI_INT(hfdi)->perf->erfType = ERROR_NOT_ENOUGH_MEMORY; + PFDI_INT(hfdi)->perf->fError = TRUE; + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto bail_and_fail; + } + ZeroMemory(fol, sizeof(struct fdi_folder)); + if (!CAB(firstfol)) CAB(firstfol) = fol; + + fol->offset = (cab_off_t) EndGetI32(buf+cffold_DataOffset); + fol->num_blocks = EndGetI16(buf+cffold_NumBlocks); + fol->comp_type = EndGetI16(buf+cffold_CompType); + + if (linkfol) + linkfol->next = fol; + linkfol = fol; + } + + /* read files */ + for (i = 0; i < fdici.cFiles; i++) { + if (PFDI_READ(hfdi, cabhf, buf, cffile_SIZEOF) != cffile_SIZEOF) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + goto bail_and_fail; + } + + file = (struct fdi_file *) PFDI_ALLOC(hfdi, sizeof(struct fdi_file)); + if (!file) { + ERR("out of memory!\n"); + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_ALLOC_FAIL; + PFDI_INT(hfdi)->perf->erfType = ERROR_NOT_ENOUGH_MEMORY; + PFDI_INT(hfdi)->perf->fError = TRUE; + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto bail_and_fail; + } + ZeroMemory(file, sizeof(struct fdi_file)); + if (!CAB(firstfile)) CAB(firstfile) = file; + + file->length = EndGetI32(buf+cffile_UncompressedSize); + file->offset = EndGetI32(buf+cffile_FolderOffset); + file->index = EndGetI16(buf+cffile_FolderIndex); + file->time = EndGetI16(buf+cffile_Time); + file->date = EndGetI16(buf+cffile_Date); + file->attribs = EndGetI16(buf+cffile_Attribs); + file->filename = FDI_read_string(hfdi, cabhf, fdici.cbCabinet); + + if (!file->filename) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + goto bail_and_fail; + } + + if (linkfile) + linkfile->next = file; + linkfile = file; + } + + for (file = CAB(firstfile); (file); file = file->next) { + + /* + * FIXME: This implementation keeps multiple cabinet files open at once + * when encountering a split cabinet. It is a quirk of this implementation + * that sometimes we decrypt the same block of data more than once, to find + * the right starting point for a file, moving the file-pointer backwards. + * If we kept a cache of certain file-pointer information, we could eliminate + * that behavior... in fact I am not sure that the caching we already have + * is not sufficient. + * + * The current implementation seems to work fine in straightforward situations + * where all the cabinet files needed for decryption are simultaneously + * available. But presumably, the API is supposed to support cabinets which + * are split across multiple CDROMS; we may need to change our implementation + * to strictly serialize it's file usage so that it opens only one cabinet + * at a time. Some experimentation with Windows is needed to figure out the + * precise semantics required. The relevant code is here and in fdi_decomp(). + */ + + /* partial-file notification */ + if ((file->index & cffileCONTINUED_FROM_PREV) == cffileCONTINUED_FROM_PREV) { + /* + * FIXME: Need to create a Cabinet with a single file spanning multiple files + * and perform some tests to figure out the right behavior. The SDK says + * FDICopy will notify the user of the filename and "disk name" (info) of + * the cabinet where the spanning file /started/. + * + * That would certainly be convenient for the API-user, who could abort, + * everything (or parallelize, if that's allowed (it is in wine)), and call + * FDICopy again with the provided filename, so as to avoid partial file + * notification and successfully unpack. This task could be quite unpleasant + * from wine's perspective: the information specifying the "start cabinet" for + * a file is associated nowhere with the file header and is not to be found in + * the cabinet header. We have only the index of the cabinet wherein the folder + * begins, which contains the file. To find that cabinet, we must consider the + * index of the current cabinet, and chain backwards, cabinet-by-cabinet (for + * each cabinet refers to its "next" and "previous" cabinet only, like a linked + * list). + * + * Bear in mind that, in the spirit of CABINET.DLL, we must assume that any + * cabinet other than the active one might be at another filepath than the + * current one, or on another CDROM. This could get rather dicey, especially + * if we imagine parallelized access to the FDICopy API. + * + * The current implementation punts -- it just returns the previous cabinet and + * it's info from the header of this cabinet. This provides the right answer in + * 95% of the cases; its worth checking if Microsoft cuts the same corner before + * we "fix" it. + */ + ZeroMemory(&fdin, sizeof(FDINOTIFICATION)); + fdin.pv = pvUser; + fdin.psz1 = (char *)file->filename; + fdin.psz2 = (CAB(mii).prevname) ? CAB(mii).prevname : &emptystring; + fdin.psz3 = (CAB(mii).previnfo) ? CAB(mii).previnfo : &emptystring; + + if (((*pfnfdin)(fdintPARTIAL_FILE, &fdin))) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_USER_ABORT; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + goto bail_and_fail; + } + /* I don't think we are supposed to decompress partial files. This prevents it. */ + file->oppressed = TRUE; + } + if (file->oppressed) { + filehf = 0; + } else { + ZeroMemory(&fdin, sizeof(FDINOTIFICATION)); + fdin.pv = pvUser; + fdin.psz1 = (char *)file->filename; + fdin.cb = file->length; + fdin.date = file->date; + fdin.time = file->time; + fdin.attribs = file->attribs; + if ((filehf = ((*pfnfdin)(fdintCOPY_FILE, &fdin))) == -1) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_USER_ABORT; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + goto bail_and_fail; + } + } + + /* find the folder for this file if necc. */ + if (filehf) { + int i2; + + fol = CAB(firstfol); + if ((file->index & cffileCONTINUED_TO_NEXT) == cffileCONTINUED_TO_NEXT) { + /* pick the last folder */ + while (fol->next) fol = fol->next; + } else { + for (i2 = 0; (i2 < file->index); i2++) + if (fol->next) /* bug resistance, should always be true */ + fol = fol->next; + } + } + + if (filehf) { + cab_UWORD comptype = fol->comp_type; + int ct1 = comptype & cffoldCOMPTYPE_MASK; + int ct2 = CAB(current) ? (CAB(current)->comp_type & cffoldCOMPTYPE_MASK) : 0; + int err = 0; + + TRACE("Extracting file %s as requested by callee.\n", debugstr_a(file->filename)); + + /* set up decomp_state */ + CAB(hfdi) = hfdi; + CAB(filehf) = filehf; + CAB(cabhf) = cabhf; + + /* Was there a change of folder? Compression type? Did we somehow go backwards? */ + if ((ct1 != ct2) || (CAB(current) != fol) || (file->offset < CAB(offset))) { + + TRACE("Resetting folder for file %s.\n", debugstr_a(file->filename)); + + /* free stuff for the old decompresser */ + switch (ct2) { + case cffoldCOMPTYPE_LZX: + if (LZX(window)) { + PFDI_FREE(hfdi, LZX(window)); + LZX(window) = NULL; + } + break; + case cffoldCOMPTYPE_QUANTUM: + if (QTM(window)) { + PFDI_FREE(hfdi, QTM(window)); + QTM(window) = NULL; + } + break; + } + + CAB(decomp_cab) = NULL; + PFDI_SEEK(CAB(hfdi), CAB(cabhf), fol->offset, SEEK_SET); + CAB(offset) = 0; + CAB(outlen) = 0; + + /* initialize the new decompresser */ + switch (ct1) { + case cffoldCOMPTYPE_NONE: + CAB(decompress) = NONEfdi_decomp; + break; + case cffoldCOMPTYPE_MSZIP: + CAB(decompress) = ZIPfdi_decomp; + break; + case cffoldCOMPTYPE_QUANTUM: + CAB(decompress) = QTMfdi_decomp; + err = QTMfdi_init((comptype >> 8) & 0x1f, (comptype >> 4) & 0xF, decomp_state); + break; + case cffoldCOMPTYPE_LZX: + CAB(decompress) = LZXfdi_decomp; + err = LZXfdi_init((comptype >> 8) & 0x1f, decomp_state); + break; + default: + err = DECR_DATAFORMAT; + } + } + + CAB(current) = fol; + + switch (err) { + case DECR_OK: + break; + case DECR_NOMEMORY: + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_ALLOC_FAIL; + PFDI_INT(hfdi)->perf->erfType = ERROR_NOT_ENOUGH_MEMORY; + PFDI_INT(hfdi)->perf->fError = TRUE; + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto bail_and_fail; + default: + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET; + PFDI_INT(hfdi)->perf->erfOper = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + goto bail_and_fail; + } + + if (file->offset > CAB(offset)) { + /* decode bytes and send them to /dev/null */ + switch ((err = fdi_decomp(file, 0, decomp_state, pszCabPath, pfnfdin, pvUser))) { + case DECR_OK: + break; + case DECR_USERABORT: + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_USER_ABORT; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + goto bail_and_fail; + case DECR_NOMEMORY: + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_ALLOC_FAIL; + PFDI_INT(hfdi)->perf->erfType = ERROR_NOT_ENOUGH_MEMORY; + PFDI_INT(hfdi)->perf->fError = TRUE; + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto bail_and_fail; + default: + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET; + PFDI_INT(hfdi)->perf->erfOper = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + goto bail_and_fail; + } + CAB(offset) = file->offset; + } + + /* now do the actual decompression */ + err = fdi_decomp(file, 1, decomp_state, pszCabPath, pfnfdin, pvUser); + if (err) CAB(current) = NULL; else CAB(offset) += file->length; + + switch (err) { + case DECR_OK: + break; + case DECR_USERABORT: + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_USER_ABORT; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + goto bail_and_fail; + case DECR_NOMEMORY: + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_ALLOC_FAIL; + PFDI_INT(hfdi)->perf->erfType = ERROR_NOT_ENOUGH_MEMORY; + PFDI_INT(hfdi)->perf->fError = TRUE; + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto bail_and_fail; + default: + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET; + PFDI_INT(hfdi)->perf->erfOper = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + goto bail_and_fail; + } + + /* fdintCLOSE_FILE_INFO notification */ + ZeroMemory(&fdin, sizeof(FDINOTIFICATION)); + fdin.pv = pvUser; + fdin.psz1 = (char *)file->filename; + fdin.hf = filehf; + fdin.cb = (file->attribs & cffile_A_EXEC) ? TRUE : FALSE; /* FIXME: is that right? */ + fdin.date = file->date; + fdin.time = file->time; + fdin.attribs = file->attribs; /* FIXME: filter _A_EXEC? */ + err = ((*pfnfdin)(fdintCLOSE_FILE_INFO, &fdin)); + if (err == FALSE || err == -1) { + /* + * SDK states that even though they indicated failure, + * we are not supposed to try and close the file, so we + * just treat this like all the others + */ + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_USER_ABORT; + PFDI_INT(hfdi)->perf->erfType = 0; + PFDI_INT(hfdi)->perf->fError = TRUE; + goto bail_and_fail; + } + } + } + + /* free decompression temps */ + switch (fol->comp_type & cffoldCOMPTYPE_MASK) { + case cffoldCOMPTYPE_LZX: + if (LZX(window)) { + PFDI_FREE(hfdi, LZX(window)); + LZX(window) = NULL; + } + break; + case cffoldCOMPTYPE_QUANTUM: + if (QTM(window)) { + PFDI_FREE(hfdi, QTM(window)); + QTM(window) = NULL; + } + break; + } + + while (decomp_state) { + fdi_decomp_state *prev_fds; + + PFDI_CLOSE(hfdi, CAB(cabhf)); + + /* free the storage remembered by mii */ + if (CAB(mii).nextname) PFDI_FREE(hfdi, CAB(mii).nextname); + if (CAB(mii).nextinfo) PFDI_FREE(hfdi, CAB(mii).nextinfo); + if (CAB(mii).prevname) PFDI_FREE(hfdi, CAB(mii).prevname); + if (CAB(mii).previnfo) PFDI_FREE(hfdi, CAB(mii).previnfo); + + while (CAB(firstfol)) { + fol = CAB(firstfol); + CAB(firstfol) = CAB(firstfol)->next; + PFDI_FREE(hfdi, fol); + } + while (CAB(firstfile)) { + file = CAB(firstfile); + if (file->filename) PFDI_FREE(hfdi, (void *)file->filename); + CAB(firstfile) = CAB(firstfile)->next; + PFDI_FREE(hfdi, file); + } + prev_fds = decomp_state; + decomp_state = CAB(next); + if (prev_fds != &_decomp_state) + PFDI_FREE(hfdi, prev_fds); + } + + return TRUE; + + bail_and_fail: /* here we free ram before error returns */ + + /* free decompression temps */ + switch (fol->comp_type & cffoldCOMPTYPE_MASK) { + case cffoldCOMPTYPE_LZX: + if (LZX(window)) { + PFDI_FREE(hfdi, LZX(window)); + LZX(window) = NULL; + } + break; + case cffoldCOMPTYPE_QUANTUM: + if (QTM(window)) { + PFDI_FREE(hfdi, QTM(window)); + QTM(window) = NULL; + } + break; + } + + while (decomp_state) { + fdi_decomp_state *prev_fds; + + PFDI_CLOSE(hfdi, CAB(cabhf)); + + /* free the storage remembered by mii */ + if (CAB(mii).nextname) PFDI_FREE(hfdi, CAB(mii).nextname); + if (CAB(mii).nextinfo) PFDI_FREE(hfdi, CAB(mii).nextinfo); + if (CAB(mii).prevname) PFDI_FREE(hfdi, CAB(mii).prevname); + if (CAB(mii).previnfo) PFDI_FREE(hfdi, CAB(mii).previnfo); + + while (CAB(firstfol)) { + fol = CAB(firstfol); + CAB(firstfol) = CAB(firstfol)->next; + PFDI_FREE(hfdi, fol); + } + while (CAB(firstfile)) { + file = CAB(firstfile); + if (file->filename) PFDI_FREE(hfdi, (void *)file->filename); + CAB(firstfile) = CAB(firstfile)->next; + PFDI_FREE(hfdi, file); + } + prev_fds = decomp_state; + decomp_state = CAB(next); + if (prev_fds != &_decomp_state) + PFDI_FREE(hfdi, prev_fds); + } + + return FALSE; +} + +/*********************************************************************** + * FDIDestroy (CABINET.23) + * + * Frees a handle created by FDICreate. Do /not/ call this in the middle + * of FDICopy. Only reason for failure would be an invalid handle. + * + * PARAMS + * hfdi [I] The HFDI to free + * + * RETURNS + * TRUE for success + * FALSE for failure + */ +BOOL __cdecl FDIDestroy(HFDI hfdi) +{ + TRACE("(hfdi == ^%p)\n", hfdi); + if (REALLY_IS_FDI(hfdi)) { + PFDI_INT(hfdi)->FDI_Intmagic = 0; /* paranoia */ + PFDI_FREE(hfdi, hfdi); /* confusing, but correct */ + return TRUE; + } else { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } +} + +/*********************************************************************** + * FDITruncateCabinet (CABINET.24) + * + * Undocumented and unimplemented. + */ +BOOL __cdecl FDITruncateCabinet( + HFDI hfdi, + char *pszCabinetName, + USHORT iFolderToDelete) +{ + FIXME("(hfdi == ^%p, pszCabinetName == %s, iFolderToDelete == %hu): stub\n", + hfdi, debugstr_a(pszCabinetName), iFolderToDelete); + + if (!REALLY_IS_FDI(hfdi)) { + SetLastError(ERROR_INVALID_HANDLE); + return FALSE; + } + + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; +}