relative to cvs. Finally, I think I've nailed down the blasted split cabs. I would like to produce some tests, but otherwise, I plan to give myself a break from cabinet.dll for now, unless there are bugs to fix. Tom: you can put cabinet.dll at 80% now... and I will be working on your todo-list next. Gerold: I can't find my O2K discs... could you test the Office 2000 installer again? I will test OXP when I find the time. License: LGPL (sorry) ChangeLog: * dlls/cabinet: cabextract.c, cabinet.h, fdi.c: Greg Turner <gmturner007@ameritech.net> - eliminate pesky global variables; should be threadsafe now. - fix more memory leaks - fix some bugs - some decruftification - implement support for split cabinets and "NEXT_CABINET" notification -- diff -ur --minimal --exclude-from=/home/greg/bin/winetreediff_excl ../wine.vanilla/dlls/cabinet/cabextract.c ./dlls/cabinet/cabextract.c --- ../wine.vanilla/dlls/cabinet/cabextract.c 2003-06-19 14:48:24.000000000 -0500 +++ ./dlls/cabinet/cabextract.c 2003-07-18 15:16:46.000000000 -0500 @@ -1123,10 +1123,6 @@ /* This decruncher was researched and implemented by Matthew Russoto. */ /* It has since been tidied up by Stuart Caie */ -/* FIXME: eliminate global variables */ -static cab_UBYTE q_length_base[27], q_length_extra[27], q_extra_bits[42]; -static cab_ULONG q_position_base[42]; - /****************************************************************** * QTMinitmodel (internal) * @@ -1169,12 +1165,12 @@ /* initialise static slot/extrabits tables */ for (i = 0, j = 0; i < 27; i++) { - q_length_extra[i] = (i == 26) ? 0 : (i < 2 ? 0 : i - 2) >> 2; - q_length_base[i] = j; j += 1 << ((i == 26) ? 5 : q_length_extra[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++) { - q_extra_bits[i] = (i < 2 ? 0 : i-2) >> 1; - q_position_base[i] = j; j += 1 << q_extra_bits[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 */ @@ -1312,27 +1308,27 @@ case 4: /* selector 4 = fixed length of 3 */ GET_SYMBOL(model4, sym); - Q_READ_BITS(extra, q_extra_bits[sym]); - match_offset = q_position_base[sym] + extra + 1; + 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, q_extra_bits[sym]); - match_offset = q_position_base[sym] + extra + 1; + 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, q_length_extra[sym]); - match_length = q_length_base[sym] + extra + 5; + 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, q_extra_bits[sym]); - match_offset = q_position_base[sym] + extra + 1; + Q_READ_BITS(extra, CAB(q_extra_bits)[sym]); + match_offset = CAB(q_position_base)[sym] + extra + 1; break; default: @@ -1434,10 +1430,6 @@ * - lzx_extra_bits states how many bits of offset-from-base data is needed. */ -/* FIXME: Eliminate global variables */ -static cab_ULONG lzx_position_base[51]; -static cab_UBYTE extra_bits[51]; - /************************************************************ * LZXinit (internal) */ @@ -1460,12 +1452,12 @@ /* initialise static tables */ for (i=0, j=0; i <= 50; i += 2) { - extra_bits[i] = extra_bits[i+1] = j; /* 0,0,0,0,1,1,2,2,3,3... */ + 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++) { - lzx_position_base[i] = j; /* 0,1,2,3,4,6,8,12,16,24,32,... */ - j += 1 << extra_bits[i]; /* 1,1,1,1,2,2,4,4,8,8,16,16,32,32,... */ + 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 */ @@ -1473,7 +1465,7 @@ else if (window == 21) posn_slots = 50; else posn_slots = window << 1; - /*posn_slots=i=0; while (i < wndsize) i += 1 << extra_bits[posn_slots++]; */ + /*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); @@ -1757,9 +1749,9 @@ if (match_offset > 2) { /* not repeated offset */ if (match_offset != 3) { - extra = extra_bits[match_offset]; + extra = CAB(extra_bits)[match_offset]; READ_BITS(verbatim_bits, extra); - match_offset = lzx_position_base[match_offset] + match_offset = CAB(lzx_position_base)[match_offset] - 2 + verbatim_bits; } else { @@ -1830,8 +1822,8 @@ if (match_offset > 2) { /* not repeated offset */ - extra = extra_bits[match_offset]; - match_offset = lzx_position_base[match_offset] - 2; + extra = CAB(extra_bits)[match_offset]; + match_offset = CAB(lzx_position_base)[match_offset] - 2; if (extra > 3) { /* verbatim and aligned bits */ extra -= 3; diff -ur --minimal --exclude-from=/home/greg/bin/winetreediff_excl ../wine.vanilla/dlls/cabinet/cabinet.h ./dlls/cabinet/cabinet.h --- ../wine.vanilla/dlls/cabinet/cabinet.h 2003-06-19 14:48:24.000000000 -0500 +++ ./dlls/cabinet/cabinet.h 2003-07-18 15:18:04.000000000 -0500 @@ -269,6 +269,10 @@ int (*decompress)(int, int, struct cds_forward *); /* chosen compress fn */ cab_UBYTE inbuf[CAB_INPUTMAX+2]; /* +2 for lzx bitbuffer overflows! */ cab_UBYTE outbuf[CAB_BLOCKMAX]; + 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]; union { struct ZIPstate zip; struct QTMstate qtm; @@ -344,6 +348,7 @@ #define DECR_CHECKSUM (4) #define DECR_INPUT (5) #define DECR_OUTPUT (6) +#define DECR_USERABORT (7) /* Bitstream reading macros (Quantum / normal byte order) * diff -ur --minimal --exclude-from=/home/greg/bin/winetreediff_excl ../wine.vanilla/dlls/cabinet/fdi.c ./dlls/cabinet/fdi.c --- ../wine.vanilla/dlls/cabinet/fdi.c 2003-07-15 20:02:50.000000000 -0500 +++ ./dlls/cabinet/fdi.c 2003-07-18 15:55:44.000000000 -0500 @@ -24,7 +24,10 @@ * cabextract.c... it would theoretically be 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 (once the FDI implementation is complete, of course). + * cabextract.c using FDI (once the FDI implementation is complete, of course). Also, + * cabextract's implementation is pretty efficient; fdi.c is, by contrast, extremely + * wasteful... + * * -gmt */ @@ -47,7 +50,6 @@ struct fdi_file { struct fdi_file *next; /* next file in sequence */ - struct fdi_folder *folder; /* folder that contains this file */ LPCSTR filename; /* output name of file */ int fh; /* open file handle or NULL */ cab_ULONG length; /* uncompressed length of file */ @@ -59,32 +61,44 @@ struct fdi_folder { struct fdi_folder *next; - cab_off_t offset[CAB_SPLITMAX]; /* offset to data blocks (32 bit) */ + 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 */ - struct fdi_file *contfile; /* the first split file */ }; /* + * 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 unneccesarily 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 conceeded 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. + * 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_UBYTE block_resv; 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 */ - cab_UWORD split; /* at which split in current folder? */ 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]; @@ -93,23 +107,20 @@ 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; -/* - * 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. - */ -typedef struct { - char *prevname, *previnfo; - char *nextname, *nextinfo; - int folder_resv, header_resv; - cab_UBYTE block_resv; -} MORE_ISCAB_INFO, *PMORE_ISCAB_INFO; - /*********************************************************************** * FDICreate (CABINET.20) */ @@ -131,10 +142,6 @@ pfnalloc, pfnfree, pfnopen, pfnread, pfnwrite, pfnclose, pfnseek, cpuType, perf); - /* PONDERME: Certainly, we cannot tolerate a missing pfnalloc, as we call it just below. - pfnfree is tested as well, for symmetry. As for the rest, should we test these - too? In a vacuum, I would say yes... but does Windows care? If not, then, I guess, - neither do we.... */ if ((!pfnalloc) || (!pfnfree)) { perf->erfOper = FDIERROR_NONE; perf->erfType = ERROR_BAD_ARGUMENTS; @@ -256,15 +263,12 @@ * process the cabinet header in the style of FDIIsCabinet, but * without the sanity checks (and bug) * - * if pmii is non-null, some info not expressed in FDICABINETINFO struct - * will be stored there... responsibility to free the enclosed stuff is - * delegated to the caller in this case. */ BOOL FDI_read_entries( - HFDI hfdi, - INT_PTR hf, - PFDICABINETINFO pfdici, - PMORE_ISCAB_INFO pmii) + 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; @@ -274,8 +278,6 @@ TRACE("(hfdi == ^%p, hf == %d, pfdici == ^%p)\n", hfdi, hf, pfdici); - if (pmii) ZeroMemory(pmii, sizeof(MORE_ISCAB_INFO)); - /* get basic offset & size info */ base_offset = FDI_getoffset(hfdi, hf); @@ -407,6 +409,8 @@ } if (flags & cfheadNEXT_CABINET) { + if (pmii) + pmii->hasnext = TRUE; nextname = FDI_read_string(hfdi, hf, cabsize); if (!nextname) { if ((flags & cfheadPREV_CABINET) && pmii) { @@ -487,10 +491,6 @@ return rv; } -/* FIXME: eliminate global variables */ -static cab_UBYTE q_length_base[27], q_length_extra[27], q_extra_bits[42]; -static cab_ULONG q_position_base[42]; - /****************************************************************** * QTMfdi_initmodel (internal) * @@ -533,12 +533,12 @@ /* initialise static slot/extrabits tables */ for (i = 0, j = 0; i < 27; i++) { - q_length_extra[i] = (i == 26) ? 0 : (i < 2 ? 0 : i - 2) >> 2; - q_length_base[i] = j; j += 1 << ((i == 26) ? 5 : q_length_extra[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++) { - q_extra_bits[i] = (i < 2 ? 0 : i-2) >> 1; - q_position_base[i] = j; j += 1 << q_extra_bits[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 */ @@ -561,10 +561,6 @@ return DECR_OK; } -/* FIXME: Eliminate global variables */ -static cab_ULONG lzx_position_base[51]; -static cab_UBYTE extra_bits[51]; - /************************************************************ * LZXfdi_init (internal) */ @@ -587,12 +583,12 @@ /* initialise static tables */ for (i=0, j=0; i <= 50; i += 2) { - extra_bits[i] = extra_bits[i+1] = j; /* 0,0,0,0,1,1,2,2,3,3... */ + 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++) { - lzx_position_base[i] = j; /* 0,1,2,3,4,6,8,12,16,24,32,... */ - j += 1 << extra_bits[i]; /* 1,1,1,1,2,2,4,4,8,8,16,16,32,32,... */ + 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 */ @@ -600,7 +596,7 @@ else if (window == 21) posn_slots = 50; else posn_slots = window << 1; - /*posn_slots=i=0; while (i < wndsize) i += 1 << extra_bits[posn_slots++]; */ + /*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); @@ -1260,27 +1256,27 @@ case 4: /* selector 4 = fixed length of 3 */ GET_SYMBOL(model4, sym); - Q_READ_BITS(extra, q_extra_bits[sym]); - match_offset = q_position_base[sym] + extra + 1; + 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, q_extra_bits[sym]); - match_offset = q_position_base[sym] + extra + 1; + 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, q_length_extra[sym]); - match_length = q_length_base[sym] + extra + 5; + 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, q_extra_bits[sym]); - match_offset = q_position_base[sym] + extra + 1; + Q_READ_BITS(extra, CAB(q_extra_bits)[sym]); + match_offset = CAB(q_position_base)[sym] + extra + 1; break; default: @@ -1504,9 +1500,9 @@ if (match_offset > 2) { /* not repeated offset */ if (match_offset != 3) { - extra = extra_bits[match_offset]; + extra = CAB(extra_bits)[match_offset]; READ_BITS(verbatim_bits, extra); - match_offset = lzx_position_base[match_offset] + match_offset = CAB(lzx_position_base)[match_offset] - 2 + verbatim_bits; } else { @@ -1577,8 +1573,8 @@ if (match_offset > 2) { /* not repeated offset */ - extra = extra_bits[match_offset]; - match_offset = lzx_position_base[match_offset] - 2; + extra = CAB(extra_bits)[match_offset]; + match_offset = CAB(lzx_position_base)[match_offset] - 2; if (extra > 3) { /* verbatim and aligned bits */ extra -= 3; @@ -1698,16 +1694,26 @@ /********************************************************** * fdi_decomp (internal) + * + * Decompress the "appropriate" number of bytes. If savemode is zero, + * do not save the output anywhere, just plow through blocks until we + * reach the starting point for fi, and remember the position of the + * cabfile pointer after we are done; otherwise, save it out, decompressing + * the number of bytes in the file specified by fi. This is also where we + * jumping to additional cabinets in the case of split cab's, and provide + * (most of) the NEXT_CABINET notification semantics described in the SDK. */ -int fdi_decomp(struct fdi_file *fi, int savemode, fdi_decomp_state *decomp_state) +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)\n", fi, savemode); + TRACE("(fi == ^%p, savemode == %d, bytes == %d)\n", fi, savemode, bytes); while (bytes > 0) { /* cando = the max number of bytes we can do */ @@ -1728,10 +1734,10 @@ 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) + if (PFDI_READ(CAB(hfdi), cab->cabhf, buf, cfdata_SIZEOF) != cfdata_SIZEOF) return DECR_INPUT; - if (PFDI_SEEK(CAB(hfdi), CAB(cabhf), CAB(block_resv), SEEK_CUR) == -1) + 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 */ @@ -1739,7 +1745,7 @@ len = EndGetI16(buf+cfdata_CompressedSize); inlen += len; if (inlen > CAB_INPUTMAX) return DECR_INPUT; - if (PFDI_READ(CAB(hfdi), CAB(cabhf), data, len) != len) + if (PFDI_READ(CAB(hfdi), cab->cabhf, data, len) != len) return DECR_INPUT; /* clear two bytes after read-in data */ @@ -1750,16 +1756,195 @@ if (cksum && cksum != checksum(buf+4, 4, checksum(data, len, 0))) return DECR_CHECKSUM; /* checksum is wrong */ - /* outlen=0 means this block was part of a split block */ 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) { - /* - cabinet_close(cab); - cab = CAB(current)->cab[++CAB(split)]; - if (!cabinet_open(cab)) return DECR_INPUT; - cabinet_seek(cab, CAB(current)->offset[CAB(split)]); */ - FIXME("split block... ack! fix this.\n"); - return DECR_INPUT; + 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 wierd 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, _O_BINARY | _O_RDONLY | _O_SEQUENTIAL, 0); + 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; + } + + /* 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; /* this should never happen */ } } @@ -1769,7 +1954,8 @@ CAB(outlen) = outlen; CAB(outpos) = CAB(outbuf); } - + + CAB(decomp_cab) = cab; return DECR_OK; } @@ -1787,14 +1973,12 @@ { FDICABINETINFO fdici; FDINOTIFICATION fdin; - MORE_ISCAB_INFO mii; int cabhf, filehf; int i, idx; char fullpath[MAX_PATH]; size_t pathlen, filenamelen; char emptystring = '\0'; cab_UBYTE buf[64]; - BOOL initialcab = TRUE; struct fdi_folder *fol = NULL, *linkfol = NULL; struct fdi_file *file = NULL, *linkfile = NULL; fdi_decomp_state _decomp_state; @@ -1846,7 +2030,7 @@ } /* check if it's really a cabfile. Note that this doesn't implement the bug */ - if (!FDI_read_entries(hfdi, cabhf, &fdici, &mii)) { + if (!FDI_read_entries(hfdi, cabhf, &fdici, &(CAB(mii)))) { ERR("FDIIsCabinet failed.\n"); PFDI_CLOSE(hfdi, cabhf); return FALSE; @@ -1857,8 +2041,8 @@ fdin.setID = fdici.setID; fdin.iCabinet = fdici.iCabinet; fdin.pv = pvUser; - fdin.psz1 = (mii.nextname) ? mii.nextname : &emptystring; - fdin.psz2 = (mii.nextinfo) ? mii.nextinfo : &emptystring; + 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))) { @@ -1868,6 +2052,9 @@ 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) { @@ -1877,8 +2064,8 @@ goto bail_and_fail; } - if (mii.folder_resv > 0) - PFDI_SEEK(hfdi, cabhf, mii.folder_resv, SEEK_CUR); + 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) { @@ -1892,7 +2079,7 @@ ZeroMemory(fol, sizeof(struct fdi_folder)); if (!CAB(firstfol)) CAB(firstfol) = fol; - fol->offset[0] = (cab_off_t) EndGetI32(buf+cffold_DataOffset); + fol->offset = (cab_off_t) EndGetI32(buf+cffold_DataOffset); fol->num_blocks = EndGetI16(buf+cffold_NumBlocks); fol->comp_type = EndGetI16(buf+cffold_CompType); @@ -1943,14 +2130,14 @@ } for (file = CAB(firstfile); (file); file = file->next) { - /* partial file notification (do it just once for the first cabinet) */ - if (initialcab && ((file->index & cffileCONTINUED_FROM_PREV) == cffileCONTINUED_FROM_PREV)) { + /* partial-file notification (do it just once for the first cabinet) */ + if ((file->index & cffileCONTINUED_FROM_PREV) == cffileCONTINUED_FROM_PREV) { /* OK, more MS bugs to simulate here, I think. I don't have a huge spanning * cabinet to test this theory on ATM, but here's the deal. The SDK says that we * are supposed to 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 consumer, who could decide to abort everything and try to start over with - * that cabinet so as not to create a front-truncated output file. Note that this + * that cabinet so as to avoid partial file notification and successfully unpack. This * task would be a horrible bitch from the implementor's (wine's) perspective: the * information is associated nowhere with the file header and is not to be found in * the cabinet header. So we would have to open the previous cabinet, and check @@ -1966,8 +2153,8 @@ ZeroMemory(&fdin, sizeof(FDINOTIFICATION)); fdin.pv = pvUser; fdin.psz1 = (char *)file->filename; - fdin.psz2 = (mii.prevname) ? mii.prevname : &emptystring; - fdin.psz3 = (mii.previnfo) ? mii.previnfo : &emptystring; + 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; @@ -1975,13 +2162,12 @@ PFDI_INT(hfdi)->perf->fError = TRUE; goto bail_and_fail; } - /* I don't think we are supposed to decompress partial files */ + /* I don't think we are supposed to decompress partial files. This prevents it. */ file->oppressed = TRUE; } if (file->oppressed) { filehf = 0; } else { - /* fdintCOPY_FILE notification (TODO: skip for spanning cab's we already should have hf) */ ZeroMemory(&fdin, sizeof(FDINOTIFICATION)); fdin.pv = pvUser; fdin.psz1 = (char *)file->filename; @@ -1997,6 +2183,21 @@ } } + /* 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; @@ -2004,14 +2205,12 @@ TRACE("Extracting file %s as requested by callee.\n", debugstr_a(file->filename)); - /* set up decomp_state (unnecessary?); at least - ignore trailing three pointers in the struct */ - ZeroMemory(decomp_state, sizeof(fdi_decomp_state) - sizeof(void*) * 3); + /* set up decomp_state */ CAB(hfdi) = hfdi; CAB(filehf) = filehf; CAB(cabhf) = cabhf; - CAB(current) = file->folder; - CAB(block_resv) = mii.block_resv; + CAB(current) = fol; + CAB(decomp_cab) = NULL; /* set up the appropriate decompressor */ switch (ct1) { @@ -2049,16 +2248,20 @@ goto bail_and_fail; } - PFDI_SEEK(CAB(hfdi), CAB(cabhf), fol->offset[0], SEEK_SET); + PFDI_SEEK(CAB(hfdi), CAB(cabhf), fol->offset, SEEK_SET); CAB(offset) = 0; CAB(outlen) = 0; - CAB(split) = 0; if (file->offset > CAB(offset)) { /* decode bytes and send them to /dev/null */ - switch ((err = fdi_decomp(file, 0, decomp_state))) { + 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; @@ -2075,12 +2278,17 @@ } /* now do the actual decompression */ - err = fdi_decomp(file, 1, decomp_state); + 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; @@ -2094,6 +2302,24 @@ goto bail_and_fail; } + /* free decompression temps */ + switch (ct1) { + 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; + default: + break; + } + /* fdintCLOSE_FILE_INFO notification */ ZeroMemory(&fdin, sizeof(FDINOTIFICATION)); fdin.pv = pvUser; @@ -2120,6 +2346,15 @@ 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; @@ -2127,6 +2362,7 @@ } while (CAB(firstfile)) { file = CAB(firstfile); + if (file->filename) PFDI_FREE(hfdi, (void *)file->filename); CAB(firstfile) = CAB(firstfile)->next; PFDI_FREE(hfdi, file); } @@ -2136,19 +2372,21 @@ PFDI_FREE(hfdi, prev_fds); } - /* free the storage remembered by mii */ - if (mii.nextname) PFDI_FREE(hfdi, mii.nextname); - if (mii.nextinfo) PFDI_FREE(hfdi, mii.nextinfo); - if (mii.prevname) PFDI_FREE(hfdi, mii.prevname); - if (mii.previnfo) PFDI_FREE(hfdi, mii.previnfo); - - PFDI_CLOSE(hfdi, cabhf); return TRUE; bail_and_fail: /* here we free ram before error returns */ 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; @@ -2156,6 +2394,7 @@ } while (CAB(firstfile)) { file = CAB(firstfile); + if (file->filename) PFDI_FREE(hfdi, (void *)file->filename); CAB(firstfile) = CAB(firstfile)->next; PFDI_FREE(hfdi, file); } @@ -2165,13 +2404,6 @@ PFDI_FREE(hfdi, prev_fds); } - /* free the storage remembered by mii */ - if (mii.nextname) PFDI_FREE(hfdi, mii.nextname); - if (mii.nextinfo) PFDI_FREE(hfdi, mii.nextinfo); - if (mii.prevname) PFDI_FREE(hfdi, mii.prevname); - if (mii.previnfo) PFDI_FREE(hfdi, mii.previnfo); - - PFDI_CLOSE(hfdi, cabhf); return FALSE; } -- "We must stop thinking of the individual and start thinking about what is best for society." -Hillary Clinton, 1993 gmt