📄 fdi.c
字号:
/*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 <stdarg.h>
#include <stdio.h>
#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;
/****************************************************************
* 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;
}
}
}
}
/*************************************************************************
* 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;
}
/*************************************************************************
* 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;
}
/***********************************************************************
* 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;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -