relative, of course, to cab_C_00. Still not particularly useful, as it doesn't decode anything, but at least now I'm protected from rm -rf. License: LGPL (sorry, Stuart Caie's IP is in there) Changelog: * dlls/cabinet: cabinet.h, fdi.c: Greg Turner <gmturner007@ameritech.net> - implement FDI{Create,Destroy,IsCabinet} - fix a typo - duplicate a bug - some blathering about code duplication - change fdi.c indentation to be consistent with cabextract.c (and how i like it ;)) -- diff -ur --minimal --exclude-from=/home/greg/bin/winetreediff_excl ../wine/dlls/cabinet/cabinet.h ./dlls/cabinet/cabinet.h --- ../wine/dlls/cabinet/cabinet.h 2003-06-06 19:39:58.000000000 -0500 +++ ./dlls/cabinet/cabinet.h 2003-06-06 15:43:03.000000000 -0500 @@ -21,6 +21,8 @@ #define __WINE_CABINET_H #include "winnt.h" +#include "fdi.h" +#include "fci.h" #define CAB_SPLITMAX (10) @@ -285,6 +287,50 @@ } methods; } cab_decomp_state; +/* _Int as in "Internal" fyi */ + +typedef struct { + unsigned int FCI_Intmagic; +} FCI_Int, *PFCI_Int; + +typedef struct { + unsigned int FDI_Intmagic; + PFNALLOC pfnalloc; + PFNFREE pfnfree; + PFNOPEN pfnopen; + PFNREAD pfnread; + PFNWRITE pfnwrite; + PFNCLOSE pfnclose; + PFNSEEK pfnseek; + PERF perf; +} FDI_Int, *PFDI_Int; + +/* cast an HFCI into a PFCI_Int */ +#define PFCI_INT(hfci) ((PFDI_Int)(hfci)) + +/* cast an HFDI into a PFDI_Int */ +#define PFDI_INT(hfdi) ((PFDI_Int)(hfdi)) + +/* quickie pfdi method invokers */ +#define PFDI_ALLOC(hfdi, size) ((*PFDI_INT(hfdi)->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) ) + /* from cabextract.c */ BOOL process_cabinet(LPCSTR cabname, LPCSTR dir, BOOL fix, BOOL lower); diff -ur --minimal --exclude-from=/home/greg/bin/winetreediff_excl ../wine/dlls/cabinet/fdi.c ./dlls/cabinet/fdi.c --- ../wine/dlls/cabinet/fdi.c 2002-12-19 15:16:56.000000000 -0600 +++ ./dlls/cabinet/fdi.c 2003-06-06 19:48:24.000000000 -0500 @@ -1,7 +1,9 @@ /* * File Decompression Interface * - * Copyright 2002 Patrik Stridvall + * Copyright 2000-2002 Stuart Caie, + * 2002 Patrik Stridvall, + * 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 @@ -16,6 +18,14 @@ * 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 (or will be) a largely redundant reimplementation of the stuff in + * 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). + * -gmt */ #include "config.h" @@ -23,8 +33,11 @@ #include "windef.h" #include "winbase.h" #include "winerror.h" +#include "stdio.h" #include "fdi.h" +#include "cabinet.h" + #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(cabinet); @@ -43,19 +56,291 @@ int cpuType, PERF perf) { - FIXME("(pfnalloc == ^%p, pfnfree == ^%p, pfnopen == ^%p, pfnread == ^%p, pfnwrite == ^%p, \ - pfnclose == ^%p, pfnseek == ^%p, cpuType == %d, perf == ^%p): stub\n", - pfnalloc, pfnfree, pfnopen, pfnread, pfnwrite, pfnclose, pfnseek, - cpuType, 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); + + /* 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 can we.... */ + if ((!pfnalloc) || (!pfnfree)) { perf->erfOper = FDIERROR_NONE; - perf->erfType = 0; + perf->erfType = ERROR_BAD_ARGUMENTS; perf->fError = TRUE; - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + 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 cab + */ +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. utterly 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 aribitrarily 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; + 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("WARNING: 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) +{ + 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, *previnfo, *nextname, *nextinfo; + + TRACE("(hfdi == ^%p, hf == %d, pfdici == ^%p)\n", hfdi, hf, pfdici); + + /* get basic offset & size info */ + base_offset = FDI_getoffset(hfdi, hf); + + if (PFDI_SEEK(hfdi, hf, 0, SEEK_END) == -1) { + 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 )) { + 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)) { + 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) { + 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("wierd cabinet detect failure: no folders in cabinet\n"); + 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("wierd cabinet detect failure: no files in cabinet\n"); + 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"); + 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)) { + WARN("bunk reserve-sizes?\n"); + 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); + folder_resv = buf[cfheadext_FolderReserved]; + block_resv = buf[cfheadext_DataReserved]; + + 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) == -1L)) { + ERR("seek failure: header_resv\n"); + 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) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; /* ? */ + PFDI_INT(hfdi)->perf->fError = TRUE; + return FALSE; + } + previnfo = FDI_read_string(hfdi, hf, cabsize); + } + + if (flags & cfheadNEXT_CABINET) { + nextname = FDI_read_string(hfdi, hf, cabsize); + if (!nextname) { + PFDI_INT(hfdi)->perf->erfOper = FDIERROR_CORRUPT_CABINET; + PFDI_INT(hfdi)->perf->erfType = 0; /* ? */ + PFDI_INT(hfdi)->perf->fError = TRUE; + return FALSE; + } + nextinfo = FDI_read_string(hfdi, hf, cabsize); + } + + /* 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) @@ -65,10 +350,39 @@ INT_PTR hf, PFDICABINETINFO pfdici) { - FIXME("(hfdi == ^%p, hf == ^%d, pfdici == ^%p): stub\n", hfdi, hf, pfdici); + BOOL rv; - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + 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); + + if (rv) + pfdici->hasnext = FALSE; /* yuck. duplicate apparent cabinet.dll bug */ + + return rv; } /*********************************************************************** @@ -83,12 +397,17 @@ PFNFDIDECRYPT pfnfdid, void *pvUser) { - FIXME("(hfdi == ^%p, pszCabinet == ^%p, pszCabPath == ^%p, flags == %0d, \ - pfnfdin == ^%p, pfnfdid == ^%p, pvUser == ^%p): stub\n", - hfdi, pszCabinet, pszCabPath, flags, pfnfdin, pfnfdid, pvUser); + FIXME("(hfdi == ^%p, pszCabinet == ^%p, pszCabPath == ^%p, flags == %0d, \ + pfnfdin == ^%p, pfnfdid == ^%p, pvUser == ^%p): stub\n", + hfdi, pszCabinet, pszCabPath, flags, pfnfdin, pfnfdid, pvUser); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + if (!REALLY_IS_FDI(hfdi)) { + SetLastError(ERROR_INVALID_HANDLE); return FALSE; + } + + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return FALSE; } /*********************************************************************** @@ -96,24 +415,33 @@ */ BOOL __cdecl FDIDestroy(HFDI hfdi) { - FIXME("(hfdi == ^%p): stub\n", hfdi); - - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + 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; + } } /*********************************************************************** - * FDICreate (CABINET.20) + * FDITruncateCabinet (CABINET.24) */ BOOL __cdecl FDITruncateCabinet( HFDI hfdi, char *pszCabinetName, USHORT iFolderToDelete) { - FIXME("(hfdi == ^%p, pszCabinetName == %s, iFolderToDelete == %hu): stub\n", - hfdi, debugstr_a(pszCabinetName), iFolderToDelete); - - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + 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; } -- "A just security to property is not afforded by that government, under which unequal taxes oppress one species of property and reward another species." --James Madison gmt