mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1626 lines
56 KiB
1626 lines
56 KiB
/*** fdi.c - Diamond File Decompression Interface
|
|
*
|
|
* Microsoft Confidential
|
|
* Copyright (C) Microsoft Corporation 1993-1994
|
|
* All Rights Reserved.
|
|
*
|
|
* Author:
|
|
* Chuck Strouss
|
|
*
|
|
* History:
|
|
* 24-jan-1994 chuckst Initial version
|
|
* 16-Mar-1994 bens Add RESERVE support
|
|
* 17-Mar-1994 bens Fix bug in CFDATA splitting introduced by RESERVE
|
|
* 21-Mar-1994 bens Use spruced up fci_int.h definitions
|
|
* 25-Mar-1994 bens Add fdintCABINET_INFO notification; reduce stack
|
|
* 07-Apr-1994 bens Add Decryption interfaces
|
|
* 02-Jun-1994 bens Added Quantum compression support
|
|
* 19-Aug-1994 bens Add cpuType parameter to FDICreate().
|
|
* 31-Jan-1994 msliger Make CreateDecompress alloc buffers before MDI/QDI
|
|
* 3-Apr-1995 jeffwe Added chaining info to FDICABINETINFO
|
|
*
|
|
* Overview:
|
|
* The File Decompression Interface is used to simplify the reading of
|
|
* Diamond cabinet files. A setup program will proceed in a manner very
|
|
* similar to the pseudo code below. An FDI context is created, the
|
|
* setup program calls FDICopy() for each cabinet to be processed. For
|
|
* each file in the cabinet, FDICopy() calls a notification callback
|
|
* routine, asking the setup program if the file should be copied.
|
|
* This call-back approach is great because it allows the cabinet file
|
|
* to be read and decompressed in an optimal manner.
|
|
*/
|
|
|
|
#include <fcntl.h>
|
|
#include <sys\stat.h>
|
|
#include <io.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "types.h"
|
|
#include "asrt.h"
|
|
#include "fdi_int.h"
|
|
#include "cabinet.h"
|
|
#include "erf.h"
|
|
#include "..\mszip\mdi.h"
|
|
#include "..\quantum\qdi.h"
|
|
#include "checksum.h"
|
|
|
|
typedef struct {
|
|
// place (long) stuff first
|
|
|
|
#ifdef ASSERT
|
|
SIGNATURE sig; // structure signature sigFDI
|
|
#endif
|
|
PERF perf;
|
|
|
|
PFNFREE pfnfree; // FDI client callback functions
|
|
PFNALLOC pfnalloc;
|
|
PFNOPEN pfnopen;
|
|
PFNREAD pfnread;
|
|
PFNWRITE pfnwrite;
|
|
PFNCLOSE pfnclose;
|
|
PFNSEEK pfnseek;
|
|
int cpuType; // CPU type (see cpuXXX in fdi_int.h)
|
|
|
|
PFNFDINOTIFY pfnfdin; // passed in on FDICopy call
|
|
PFNFDIDECRYPT pfnfdid; // passed in on FDICopy call
|
|
|
|
COFF coffFolders; // Offset of CFFOLDER start
|
|
UOFF uoffFolder; // Uncompressed offset of current CFDATA
|
|
// in current folder;
|
|
|
|
MDI_CONTEXT_HANDLE mdh; // decompress context handle
|
|
void *pvUser; // User's callback context
|
|
char *pchCompr; // pointer to compressed data buffer
|
|
char *pchUncompr; // pointer to uncompressed data buffer
|
|
|
|
CFFOLDER *pcffolder; // Dynamic due to variable RESERVE size
|
|
CFDATA *pcfdata; // Dynamic due to variable RESERVE size
|
|
void *pbCFHeaderReserve; // Reserved data for CFHEADER
|
|
|
|
// Next comes the nested structures.
|
|
|
|
CFHEADER cfheader;
|
|
CFFILE cffile;
|
|
|
|
// Now the ints
|
|
|
|
int hfCabData; // file handle of cabinet for Data reads
|
|
int hfCabFiles; // file handle of cabinet for File reads
|
|
int hfNew; // file handle we're writing to
|
|
UINT iFolder; // current folder we're operating on
|
|
UINT cbMaxUncompr; // maximum uncompressed block size
|
|
UINT cbMaxCompr; // maximum compressed block size
|
|
int fInContin; // In continuation cabinet flag
|
|
|
|
UINT cbCFHeaderReserve; // Size of CFHEADER RESERVE
|
|
UINT cbCFFolderPlusReserve; // Size of CFFOLDER + folder RESERVE
|
|
UINT cbCFDataPlusReserve; // Size of CFDATA + data RESERVE
|
|
|
|
// Now the shorts
|
|
USHORT cFilesRemain; // count of CFFILE entries remaining
|
|
USHORT cFilesSkipped; // count of CONTD_FORWARD files skipped
|
|
USHORT cCFDataRemain; // Number of CFDATA blocks left in folder
|
|
TCOMP typeCompress; // selected compression type
|
|
|
|
// Now the piggy buffers
|
|
char achName [CB_MAX_FILENAME +1];
|
|
|
|
char achCabinetFirst[CB_MAX_CABINET_NAME+1];
|
|
char achDiskFirst [CB_MAX_DISK_NAME +1];
|
|
char achCabinetNext [CB_MAX_CABINET_NAME+1];
|
|
char achDiskNext [CB_MAX_DISK_NAME +1];
|
|
|
|
char szCabPath [CB_MAX_CAB_PATH +1];
|
|
|
|
//NOTE: The following items could have been on the stack, but
|
|
// some FDI clients (NT setup, for example), are very tight on
|
|
// stack space, so we use our FDI context instead!
|
|
char szCabName [CB_MAX_CAB_PATH +1];
|
|
FDINOTIFICATION fdin; // Notifcation structure
|
|
FDIDECRYPT fdid; // Decryption structure
|
|
|
|
} FDI; /* fdi */
|
|
typedef FDI *PFDI; /* pfdi */
|
|
|
|
|
|
#define PFDIfromHFDI(hfdi) ((PFDI)(hfdi))
|
|
#define HFDIfromPFDI(pfdi) ((HFDI)(pfdi))
|
|
|
|
#ifdef ASSERT
|
|
#define sigFDI MAKESIG('F','D','I','X') // FDI signature
|
|
#define AssertFDI(pfdi) AssertStructure(pfdi,sigFDI);
|
|
#else // !ASSERT
|
|
#define AssertFDI(pfdi)
|
|
#endif // !ASSERT
|
|
|
|
|
|
//** Internal function prototypes
|
|
|
|
BOOL doCabinetInfoNotify(PFDI pfdi);
|
|
BOOL InitFolder(PFDI pfdi, UINT iFolder);
|
|
BOOL FDIReadCFFILEEntry(PFDI pfdi);
|
|
BOOL FDIReadCFDATAEntry(PFDI pfdi, UINT cbPartial);
|
|
BOOL FDIReadPSZ(char *pb, int cb, PFDI pfdi);
|
|
BOOL FDIGetFile(PFDI pfdi);
|
|
BOOL FDIGetDataBlock(PFDI pfdi);
|
|
BOOL SwitchToNewCab(PFDI pfdi);
|
|
BOOL LoginCabinet(PFDI pfdi, char *pszCabinet, USHORT setID, USHORT iCabinet);
|
|
BOOL SeekFolder(PFDI pfdi,UINT iFolder);
|
|
BOOL SetDecompressionType(TCOMP typeCompress,PFDI pfdi);
|
|
BOOL MDIDestroyDecompressionGlobal(PFDI pfdi);
|
|
BOOL MDICreateDecompressionGlobal(PFDI pfdi);
|
|
BOOL MDIResetDecompressionGlobal(PFDI pfdi);
|
|
BOOL MDIDecompressGlobal(PFDI pfdi, USHORT *pcbData);
|
|
|
|
|
|
/*** FDICreate - Create an FDI context
|
|
*
|
|
* NOTE: See fdi_int.h for entry/exit conditions.
|
|
*/
|
|
HFDI FAR DIAMONDAPI FDICreate(PFNALLOC pfnalloc,
|
|
PFNFREE pfnfree,
|
|
PFNOPEN pfnopen,
|
|
PFNREAD pfnread,
|
|
PFNWRITE pfnwrite,
|
|
PFNCLOSE pfnclose,
|
|
PFNSEEK pfnseek,
|
|
int cpuType,
|
|
PERF perf)
|
|
{
|
|
PFDI pfdi;
|
|
|
|
pfdi = (PFDI)pfnalloc(sizeof (FDI));
|
|
if (pfdi == NULL) {
|
|
ErfSetCodes(perf,FDIERROR_ALLOC_FAIL,0);
|
|
return NULL;
|
|
}
|
|
|
|
SetAssertSignature(pfdi,sigFDI);
|
|
AssertFDI(pfdi); // Make sure we've really set sig
|
|
|
|
pfdi->pfnfree = pfnfree; // Save free function in our context
|
|
pfdi->pfnalloc = pfnalloc; // and all other passed in functions
|
|
pfdi->pfnopen = pfnopen;
|
|
pfdi->pfnread = pfnread;
|
|
pfdi->pfnwrite = pfnwrite;
|
|
pfdi->pfnclose = pfnclose;
|
|
pfdi->pfnseek = pfnseek;
|
|
pfdi->cpuType = cpuType;
|
|
pfdi->perf = perf; // and error reporting structure
|
|
|
|
pfdi->typeCompress = tcompBAD; // no decompressor initialized
|
|
|
|
//** Don't know how big reserved sections are yet, so we cannot allocate
|
|
// the buffers. Set bad sizes to help catch errors. LoginCabinet
|
|
// will get the sizes from the CFHEADER/CFRESERVE structures in the
|
|
// cabinet file and will allocate the buffers.
|
|
|
|
pfdi->pcfdata = NULL;
|
|
pfdi->pcffolder = NULL;
|
|
pfdi->pbCFHeaderReserve = NULL;
|
|
|
|
pfdi->cbCFHeaderReserve = cbCF_HEADER_BAD; // Bad value
|
|
pfdi->cbCFDataPlusReserve = 0xFFFF; // Bad value
|
|
pfdi->cbCFFolderPlusReserve = 0xFFFF; // Bad value
|
|
|
|
//** Remember that cabinet file handles are closed
|
|
pfdi->hfCabFiles = -1;
|
|
pfdi->hfCabData = -1;
|
|
|
|
//** Success
|
|
return HFDIfromPFDI(pfdi);
|
|
}
|
|
|
|
|
|
/*** FDIDestroy - Destroy an FDI context
|
|
*
|
|
* NOTE: See fdi_int.h for entry/exit conditions.
|
|
*/
|
|
BOOL FAR DIAMONDAPI FDIDestroy(HFDI hfdi)
|
|
{
|
|
PFDI pfdi;
|
|
|
|
pfdi = PFDIfromHFDI (hfdi);
|
|
AssertFDI (pfdi);
|
|
|
|
SetDecompressionType(tcompBAD,pfdi); // free the decompressor
|
|
|
|
//** Free cabinet structure buffers d
|
|
if (pfdi->pbCFHeaderReserve) {
|
|
(*pfdi->pfnfree)(pfdi->pbCFHeaderReserve);
|
|
}
|
|
|
|
if (pfdi->pcffolder) {
|
|
(*pfdi->pfnfree)(pfdi->pcffolder);
|
|
}
|
|
|
|
if (pfdi->pcfdata) {
|
|
(*pfdi->pfnfree)(pfdi->pcfdata);
|
|
}
|
|
|
|
//** Make sure any open file handles are closed
|
|
if (pfdi->hfCabFiles != -1) {
|
|
pfdi->pfnclose(pfdi->hfCabFiles); // close File Cab File
|
|
}
|
|
if (pfdi->hfCabData != -1) {
|
|
pfdi->pfnclose(pfdi->hfCabData); // close Data Cab File
|
|
}
|
|
|
|
ClearAssertSignature(pfdi);
|
|
pfdi->pfnfree(pfdi); // free our data
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** FDIIsCabinet - Determines if file is a cabinet, returns info if it is
|
|
*
|
|
* NOTE: See fdi_int.h for entry/exit conditions.
|
|
*/
|
|
BOOL FAR DIAMONDAPI FDIIsCabinet(HFDI hfdi,
|
|
int hf,
|
|
PFDICABINETINFO pfdici)
|
|
{
|
|
CFHEADER cfheader;
|
|
PFDI pfdi;
|
|
|
|
pfdi = PFDIfromHFDI (hfdi);
|
|
AssertFDI(pfdi);
|
|
|
|
//** Read the CFHEADER structure
|
|
if (sizeof(CFHEADER) != pfdi->pfnread(hf,
|
|
&cfheader,
|
|
sizeof(CFHEADER))) {
|
|
return FALSE; // Too small or error on I/O
|
|
}
|
|
|
|
//** Make sure this is a cabinet file
|
|
if (cfheader.sig != sigCFHEADER) {
|
|
return FALSE; // Signature does not match
|
|
}
|
|
|
|
//** Make sure we know this version number
|
|
if (cfheader.version != verCF) {
|
|
//** Set the error in this case, so client knows version is wrong
|
|
ErfSetCodes(pfdi->perf,FDIERROR_UNKNOWN_CABINET_VERSION,
|
|
cfheader.version); // return version found
|
|
return FALSE;
|
|
}
|
|
|
|
//** Return cabinet info to caller
|
|
pfdici->cbCabinet = cfheader.cbCabinet;
|
|
pfdici->cFolders = cfheader.cFolders;
|
|
pfdici->cFiles = cfheader.cFiles;
|
|
pfdici->setID = cfheader.setID;
|
|
pfdici->iCabinet = cfheader.iCabinet;
|
|
pfdici->fReserve = (cfheader.flags & cfhdrRESERVE_PRESENT) != 0;
|
|
pfdici->hasprev = (cfheader.flags & cfhdrPREV_CABINET);
|
|
pfdici->hasnext = (cfheader.flags & cfhdrNEXT_CABINET);
|
|
return TRUE;
|
|
} /* FDIIsCabinet() */
|
|
|
|
|
|
/*** FDICopy - extracts files from a cabinet
|
|
*
|
|
* NOTE: See fdi_int.h for entry/exit conditions.
|
|
*/
|
|
BOOL FAR DIAMONDAPI FDICopy(HFDI hfdi,
|
|
char *pszCabinet,
|
|
char *pszCabPath,
|
|
int flags,
|
|
PFNFDINOTIFY pfnfdin,
|
|
PFNFDIDECRYPT pfnfdid,
|
|
void *pvUser)
|
|
{
|
|
PFDINOTIFICATION pfdin;
|
|
PFDI pfdi;
|
|
BOOL rc=FALSE; // Assume error
|
|
|
|
pfdi = PFDIfromHFDI (hfdi);
|
|
AssertFDI(pfdi);
|
|
pfdin = &pfdi->fdin;
|
|
|
|
//** Save call back info for use by other functions
|
|
pfdi->pvUser = pvUser;
|
|
pfdi->pfnfdin = pfnfdin;
|
|
pfdi->pfnfdid = pfnfdid;
|
|
|
|
pfdi->cFilesSkipped = 0; // we haven't skipped any CONTD_FOR files yet
|
|
strcpy(pfdi->szCabPath,pszCabPath); // make a local copy of cabinet path
|
|
|
|
//** Get cabinet; skip setID/iCabinet check
|
|
if (!LoginCabinet(pfdi,pszCabinet,0,iCABINET_BAD)) { // Get cabinet
|
|
goto error; // error already filled in
|
|
}
|
|
|
|
pfdi->fInContin = 0; // Not in continuation
|
|
pfdi->iFolder = 0xFFFF; // No folder being used yet
|
|
strcpy(pfdi->szCabPath,pszCabPath); // save path to cabinet
|
|
|
|
while (pfdi->cFilesRemain--) {
|
|
if (!FDIReadCFFILEEntry(pfdi)) {
|
|
goto error; // error already filled in
|
|
}
|
|
//** Now copy stuff that is needed on a notification callback
|
|
pfdin->psz1 = pfdi->achName; // pass name of the file
|
|
pfdin->cb = pfdi->cffile.cbFile; // pass size of file
|
|
pfdin->psz2 = pfdi->achCabinetFirst; // name of First Cab (if app.)
|
|
pfdin->psz3 = pfdi->achDiskFirst; // name of First Disk (if app.)
|
|
pfdin->date = pfdi->cffile.date; // pass date
|
|
pfdin->time = pfdi->cffile.time; // pass time
|
|
pfdin->attribs = pfdi->cffile.attribs; // pass attributes
|
|
pfdin->pv = pfdi->pvUser;
|
|
|
|
if (IS_CONTD_BACK(pfdi->cffile.iFolder)) { // continued back?
|
|
if (pfdi->fInContin) { // in a continuation cab?
|
|
//** We're in a continuation cabinet, and have a continued-
|
|
// back candidate. Ask the caller if he wants it, and copy
|
|
// it if so.
|
|
pfdi->hfNew = pfnfdin(fdintCOPY_FILE,pfdin);
|
|
if (pfdi->hfNew == -1) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0);
|
|
goto error;
|
|
}
|
|
if (pfdi->hfNew) {
|
|
if (!FDIGetFile(pfdi)) { // Get the file written into hfNew
|
|
goto error; // error already filled in
|
|
}
|
|
}
|
|
else { // caller elected to skip this file
|
|
if (IS_CONTD_FORWARD(pfdi->cffile.iFolder)) {
|
|
pfdi->cFilesSkipped++; // count forward files skipped
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
//** If a file is continued-back, but we're not in a
|
|
// continuation cabinet, the proper behavior is just to
|
|
// notify the caller that we're skipping the file.
|
|
if (-1 == pfnfdin(fdintPARTIAL_FILE,pfdin)) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0);
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
else { // not continued-back
|
|
if (!pfdi->fInContin) { // in original cab?
|
|
//** This is a normal (not continued-back) file in the
|
|
// non-continued cabinet, so ask the user if he wants it,
|
|
// and copy it if so.
|
|
pfdi->hfNew = pfnfdin(fdintCOPY_FILE,pfdin);
|
|
if (pfdi->hfNew == -1) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0);
|
|
goto error;
|
|
}
|
|
if (pfdi->hfNew) {
|
|
if (!FDIGetFile(pfdi)) { // Get the file written into hfNew
|
|
goto error; // error already filled in
|
|
}
|
|
}
|
|
else { // caller elected to skip this file
|
|
if (IS_CONTD_FORWARD(pfdi->cffile.iFolder)) {
|
|
pfdi->cFilesSkipped++; // count forward files skipped
|
|
}
|
|
}
|
|
} // ignore non-continued-back files in continuation cabs
|
|
}
|
|
} // end while cFilesRemain
|
|
|
|
//** Success
|
|
rc = TRUE;
|
|
|
|
error:
|
|
//** Ignore errors closing cabinet files. There is potential that this
|
|
// error may occur if a network goes down, etc., and there is no harm
|
|
// done.
|
|
if (pfdi->hfCabFiles != -1) {
|
|
pfdi->pfnclose(pfdi->hfCabFiles); // close File Cab File
|
|
}
|
|
|
|
if (pfdi->hfCabData != -1) {
|
|
pfdi->pfnclose(pfdi->hfCabData); // close Data Cab File
|
|
}
|
|
|
|
//** Remember that cabinet file handles are closed
|
|
pfdi->hfCabFiles = -1;
|
|
pfdi->hfCabData = -1;
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*** LoginCabinet - Make a cabinet current
|
|
*
|
|
* Entry:
|
|
* pfdi - pointer to FDI context
|
|
* pszCabinet - pointer to cabinet path/name
|
|
* setID - required setID (only if iCabinet == iCABINET_BAD)
|
|
* iCabinet - required iCabinet; check that setID/iCabinet match
|
|
* cabinet; Set iCabinet == iCABINET_BAD to skip this check.
|
|
*
|
|
* Exit-Success:
|
|
* returns TRUE
|
|
* pfdi->cfheader is filled in
|
|
* pfdi->hfCabFiles points to first CFFILE entry
|
|
* pfdi->cFilesRemain initialized from CFHEADER
|
|
*
|
|
* Exit-Failure:
|
|
* returns FALSE, cabinet was not requested setID/iCabinet, or other
|
|
* errors -- perr filled in.
|
|
*/
|
|
BOOL LoginCabinet(PFDI pfdi, char *pszCabinet, USHORT setID, USHORT iCabinet)
|
|
{
|
|
CFHEADER cfheader;
|
|
CFRESERVE cfreserve;
|
|
UINT cbCFFolderPlusReserve;
|
|
UINT cbCFDataPlusReserve;
|
|
|
|
AssertFDI(pfdi);
|
|
|
|
strcpy(pfdi->szCabName,pfdi->szCabPath); // build path to cabinet
|
|
strcat(pfdi->szCabName,pszCabinet);
|
|
|
|
Assert(pfdi->hfCabFiles == -1);
|
|
Assert(pfdi->hfCabData == -1);
|
|
|
|
/** NOTE: It is very important to avoid changing any globals (besides
|
|
* the cabinet file handles) until we have verified this cabinet
|
|
* is the requested one!
|
|
*/
|
|
if ((-1 == (pfdi->hfCabFiles=pfdi->pfnopen(pfdi->szCabName,
|
|
_O_BINARY | _O_RDONLY,
|
|
_S_IREAD | _S_IWRITE )))
|
|
|
|
// Yes, Virginia, we're going to open the same file again, so that
|
|
// we can read the CFFILE and CFDATA streams separately, and so we
|
|
// can switch to a different cabinet for the remaining data streams
|
|
// We could probably use dup() instead, but that would require our
|
|
// caller to support it as a callback, so we'll just do another open.
|
|
|
|
|| (-1 == (pfdi->hfCabData=pfdi->pfnopen(pfdi->szCabName,
|
|
_O_BINARY | _O_RDONLY,
|
|
_S_IREAD | _S_IWRITE )))) {
|
|
//** We don't have a specfic error code from the caller's function
|
|
ErfSetCodes(pfdi->perf,FDIERROR_CABINET_NOT_FOUND,0);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Read the CFHEADER structure
|
|
if (sizeof(CFHEADER) != pfdi->pfnread(pfdi->hfCabFiles,
|
|
&cfheader,
|
|
sizeof(CFHEADER))) {
|
|
//** We don't have a specfic error code from the caller's function
|
|
ErfSetCodes(pfdi->perf,FDIERROR_NOT_A_CABINET,0);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Make sure this is a cabinet file
|
|
if (cfheader.sig != sigCFHEADER) {
|
|
//** We don't have a specfic error code from the caller's function
|
|
ErfSetCodes(pfdi->perf,FDIERROR_NOT_A_CABINET,0);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Make sure we know this version number
|
|
if (cfheader.version != verCF) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_UNKNOWN_CABINET_VERSION,
|
|
cfheader.version); // return version found
|
|
return FALSE;
|
|
}
|
|
|
|
//** Check setID/iCabinet, if we are asked to
|
|
if ((iCabinet != iCABINET_BAD) && // Need to to check setID/iCabinet
|
|
((setID != cfheader.setID) || // SetIDs don't match
|
|
(iCabinet != cfheader.iCabinet))) { // or cabinet numbers don't match
|
|
//** Not the cabinet that was requested
|
|
ErfSetCodes(pfdi->perf,FDIERROR_WRONG_CABINET,0);
|
|
return FALSE;
|
|
}
|
|
|
|
/** OK, this cabinet looks like the one we want.
|
|
* Store the cfheader in our state structure.
|
|
*/
|
|
pfdi->cfheader = cfheader;
|
|
|
|
//** Assume there is no CFRESERVE section
|
|
cfreserve.cbCFHeader = 0;
|
|
cfreserve.cbCFFolder = 0;
|
|
cfreserve.cbCFData = 0;
|
|
|
|
//** Now read in the CFRESERVE section (if present)
|
|
if (pfdi->cfheader.flags & cfhdrRESERVE_PRESENT) {
|
|
//** Read the CFRESERVE first
|
|
if (sizeof(CFRESERVE) != pfdi->pfnread(pfdi->hfCabFiles,
|
|
&cfreserve,
|
|
sizeof(CFRESERVE))) {
|
|
//** We don't have a specfic error code from the caller's function
|
|
ErfSetCodes(pfdi->perf,FDIERROR_NOT_A_CABINET,0);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Make sure CFRESERVE fields seem valid
|
|
Assert(cfreserve.cbCFHeader <= cbRESERVE_HEADER_MAX);
|
|
Assert(cfreserve.cbCFFolder <= cbRESERVE_FOLDER_MAX);
|
|
Assert(cfreserve.cbCFData <= cbRESERVE_DATA_MAX);
|
|
|
|
//** Allocate buffer for reserved portion of CF header, if necessary
|
|
if (pfdi->cbCFHeaderReserve == cbCF_HEADER_BAD) {
|
|
//** First header we are reading
|
|
pfdi->cbCFHeaderReserve = cfreserve.cbCFHeader; // Remember size
|
|
if (pfdi->cbCFHeaderReserve > 0) { // Need to allocate buffer
|
|
if (!(pfdi->pbCFHeaderReserve =
|
|
(*pfdi->pfnalloc)(pfdi->cbCFHeaderReserve))) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_ALLOC_FAIL,0);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//** Read RESERVE data area for CFHEADER, if present
|
|
if (pfdi->cbCFHeaderReserve > 0) { // Need to read reserved data
|
|
if (pfdi->cbCFHeaderReserve != pfdi->pfnread(pfdi->hfCabFiles,
|
|
pfdi->pbCFHeaderReserve,
|
|
pfdi->cbCFHeaderReserve)) {
|
|
//** No specfic error code from the caller's function
|
|
ErfSetCodes(pfdi->perf,FDIERROR_NOT_A_CABINET,0);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//** Compute size of CFFOLDER buffer and allocate it if necessary
|
|
cbCFFolderPlusReserve = cfreserve.cbCFFolder + sizeof(CFFOLDER);
|
|
if (!pfdi->pcffolder) { // First time, need to allocate buffer
|
|
pfdi->cbCFFolderPlusReserve = cbCFFolderPlusReserve; // Full size
|
|
if (!(pfdi->pcffolder = (*pfdi->pfnalloc)(pfdi->cbCFFolderPlusReserve))) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_ALLOC_FAIL,0);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
//** Make sure this folder has same reserve size as last folder!
|
|
if (cbCFFolderPlusReserve != pfdi->cbCFFolderPlusReserve) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_RESERVE_MISMATCH,0);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//** Compute size of CFDATA buffer and allocate it if necessary
|
|
// NOTE: It is important not to touch the CFDATA block if already
|
|
// allocated, because our caller depends upon the old data
|
|
// staying there across cabinet boundaries in order to handle
|
|
// CFDATA blocks that are split across cabinet boundaries!
|
|
//
|
|
cbCFDataPlusReserve = cfreserve.cbCFData + sizeof(CFDATA);
|
|
if (!pfdi->pcfdata) { // First time, need to allocate buffer
|
|
pfdi->cbCFDataPlusReserve = cbCFDataPlusReserve; // Full size
|
|
if (!(pfdi->pcfdata = (*pfdi->pfnalloc)(pfdi->cbCFDataPlusReserve))) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_ALLOC_FAIL,0);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
//** Make sure this Data has same reserve size as last Data!
|
|
if (cbCFDataPlusReserve != pfdi->cbCFDataPlusReserve) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_RESERVE_MISMATCH,0);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//** Now read in the back/forward continuation fields, if present
|
|
if (pfdi->cfheader.flags & cfhdrPREV_CABINET) {
|
|
if (!FDIReadPSZ(pfdi->achCabinetFirst,CB_MAX_CABINET_NAME,pfdi)
|
|
|| !FDIReadPSZ(pfdi->achDiskFirst,CB_MAX_DISK_NAME,pfdi)) {
|
|
return FALSE; // Error already filled in
|
|
}
|
|
}
|
|
else {
|
|
//** No previous disk/cabinet
|
|
pfdi->achCabinetFirst[0] = '\0';
|
|
pfdi->achDiskFirst[0] = '\0';
|
|
}
|
|
|
|
if (pfdi->cfheader.flags & cfhdrNEXT_CABINET) {
|
|
if (!FDIReadPSZ(pfdi->achCabinetNext,CB_MAX_CABINET_NAME,pfdi)
|
|
|| !FDIReadPSZ(pfdi->achDiskNext,CB_MAX_DISK_NAME,pfdi)) {
|
|
return FALSE; // Error already filled in
|
|
}
|
|
}
|
|
else {
|
|
//** No next disk/cabinet
|
|
pfdi->achCabinetNext[0] = '\0';
|
|
pfdi->achDiskNext[0] = '\0';
|
|
}
|
|
|
|
//** Remember base of folder entries
|
|
pfdi->coffFolders = pfdi->pfnseek(pfdi->hfCabFiles,0L,SEEK_CUR);
|
|
if (-1L == pfdi->coffFolders) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Seek to first CFFILE entry
|
|
if (-1L == pfdi->pfnseek(pfdi->hfCabFiles,
|
|
pfdi->cfheader.coffFiles,
|
|
SEEK_SET)) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0);
|
|
return FALSE;
|
|
}
|
|
|
|
pfdi->cFilesRemain = pfdi->cfheader.cFiles;
|
|
|
|
//** Tell client what the *next* cabinet is
|
|
if (!doCabinetInfoNotify(pfdi)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//** Success
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** FDIGetFile - Extract one individual file from cabinet.
|
|
*
|
|
* Entry:
|
|
* pfdi - pointer to FDI context
|
|
* pfdi->hfNew - open file handle to write to
|
|
* pfdi->cffile - filled in
|
|
*
|
|
* Exit-Success:
|
|
* returns TRUE, output file closed
|
|
*
|
|
* Exit-Failure:
|
|
* returns FALSE, output file closed, error structure filled in
|
|
*/
|
|
BOOL FDIGetFile(PFDI pfdi)
|
|
{
|
|
UOFF uoffFile;
|
|
UOFF cbFileRemain;
|
|
UINT cbWriteBase;
|
|
UINT cbWrite;
|
|
PFDINOTIFICATION pfdin;
|
|
int rcClose;
|
|
|
|
//** First, we must make sure we have selected the correct folder
|
|
AssertFDI(pfdi);
|
|
if (!InitFolder(pfdi,pfdi->cffile.iFolder)) {
|
|
goto error; // error code already filled in
|
|
}
|
|
|
|
uoffFile = pfdi->cffile.uoffFolderStart; // init file pointer
|
|
cbFileRemain = pfdi->cffile.cbFile; // init file counter
|
|
|
|
|
|
//jeffwe: Only bypass if there is something to bypass
|
|
if (cbFileRemain) {
|
|
//** Now bypass any unwanted data blocks
|
|
while (uoffFile >= (pfdi->uoffFolder + pfdi->pcfdata->cbUncomp)) {
|
|
if (!FDIGetDataBlock(pfdi)) { // get next data block
|
|
goto error; // error code already filled in
|
|
}
|
|
}
|
|
}
|
|
|
|
//** Okay, now we have the first block. Write it, and any that
|
|
// remain, into our target file
|
|
while (cbFileRemain) {
|
|
cbWriteBase = (UINT)(uoffFile - pfdi->uoffFolder);
|
|
cbWrite = pfdi->pcfdata->cbUncomp - cbWriteBase; // size left in block
|
|
if ((ULONG)cbWrite > cbFileRemain) {
|
|
cbWrite = (UINT)cbFileRemain;
|
|
}
|
|
|
|
if (cbWrite != pfdi->pfnwrite(pfdi->hfNew,
|
|
&pfdi->pchUncompr[cbWriteBase],
|
|
cbWrite)) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_TARGET_FILE,0);
|
|
goto error; // couldn't write
|
|
}
|
|
|
|
uoffFile += cbWrite;
|
|
cbFileRemain -= cbWrite;
|
|
|
|
//** Get another block?
|
|
if (cbFileRemain && !FDIGetDataBlock(pfdi)) {
|
|
goto error; // error code already filled in
|
|
}
|
|
}
|
|
|
|
pfdin = &pfdi->fdin;
|
|
pfdin->psz1 = pfdi->achName; // pass name of file
|
|
pfdin->hf = pfdi->hfNew; // pass file handle
|
|
pfdin->date = pfdi->cffile.date; // pass date
|
|
pfdin->time = pfdi->cffile.time; // pass time
|
|
pfdin->attribs = pfdi->cffile.attribs; // pass attributes
|
|
pfdin->pv = pfdi->pvUser; // pass callback context
|
|
pfdin->cb = 0; // Default as don't run after ext.
|
|
|
|
if (pfdin->attribs & RUNATTRIB) {
|
|
pfdin->cb = 1; // Run This after extraction
|
|
pfdin->attribs &= ~RUNATTRIB; // Clear the flag
|
|
}
|
|
|
|
|
|
//** Let client close file and set file date/time/attributes
|
|
rcClose = pfdi->pfnfdin(fdintCLOSE_FILE_INFO,pfdin);
|
|
if (rcClose == -1) {
|
|
//** NOTE: We assume that the client *did* close the file!
|
|
ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0);
|
|
goto error;
|
|
}
|
|
pfdi->hfNew = -1; // Remember that file is closed
|
|
|
|
if (!rcClose) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_TARGET_FILE,0);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
|
|
error:
|
|
//** Make sure output file is closed
|
|
if (pfdi->hfNew != -1) {
|
|
pfdi->pfnclose(pfdi->hfNew); // Close it
|
|
pfdi->hfNew = -1; // Remember that file is closed
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*** FDIGetDataBlock -- read next CFDATA entry, decompress
|
|
*
|
|
* Entry:
|
|
* pfdi -- FDI context
|
|
*
|
|
* Exit-success:
|
|
* returns TRUE;
|
|
* pfdi->uoffFolder has *previous* block added to it
|
|
* new data block ready in pfdi->pchUncompr
|
|
*
|
|
* Exit-failure:
|
|
* returns FALSE, error code filled in
|
|
*/
|
|
BOOL FDIGetDataBlock(PFDI pfdi)
|
|
{
|
|
USHORT cbResult;
|
|
|
|
AssertFDI(pfdi);
|
|
pfdi->uoffFolder += pfdi->pcfdata->cbUncomp; // update uncompr base ptr
|
|
|
|
if (pfdi->cCFDataRemain == 0) {
|
|
if (!SwitchToNewCab(pfdi)) {
|
|
return FALSE; // error already filled in
|
|
}
|
|
}
|
|
|
|
pfdi->cCFDataRemain--;
|
|
if (!FDIReadCFDATAEntry(pfdi,0)) {
|
|
return FALSE; // error already filled in
|
|
}
|
|
|
|
//** Is this a continued-forward block?
|
|
if (pfdi->pcfdata->cbUncomp == 0) {
|
|
//** Switch to new cabinet and read second piece of data block
|
|
if ((!SwitchToNewCab(pfdi))
|
|
|| (!FDIReadCFDATAEntry(pfdi,pfdi->pcfdata->cbData))) {
|
|
return FALSE; // error already filled in
|
|
}
|
|
}
|
|
|
|
cbResult = pfdi->pcfdata->cbUncomp; // Expected uncompressed size
|
|
if (!MDIDecompressGlobal(pfdi,&cbResult)) {
|
|
return FALSE; // error already filled in
|
|
}
|
|
|
|
if (cbResult != pfdi->pcfdata->cbUncomp) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0);
|
|
return FALSE; // wrong length after decompress
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** SwitchToNewCab -- move on to the continuation cabinet
|
|
*
|
|
* Entry:
|
|
* pfdi -- FDI context pointer
|
|
*
|
|
* Exit-success:
|
|
* returns TRUE
|
|
*
|
|
* Exit-failure:
|
|
* returns FALSE, error filled in
|
|
*/
|
|
BOOL SwitchToNewCab(PFDI pfdi)
|
|
{
|
|
BOOL fWrongCabinet;
|
|
USHORT iCabinet;
|
|
PFDINOTIFICATION pfdin;
|
|
USHORT setID;
|
|
|
|
AssertFDI(pfdi);
|
|
Assert(pfdi->hfCabData != -1);
|
|
Assert(pfdi->hfCabFiles != -1);
|
|
|
|
//** Remember cabinet info so we can make sure we get the correct
|
|
// continuation cabinet
|
|
setID = pfdi->cfheader.setID;
|
|
iCabinet = pfdi->cfheader.iCabinet + 1;
|
|
|
|
pfdin = &pfdi->fdin;
|
|
pfdin->psz1 = pfdi->achCabinetNext; // pass name of next cab
|
|
pfdin->psz2 = pfdi->achDiskNext; // pass name of next disk
|
|
pfdin->psz3 = pfdi->szCabPath; // allow cabinet path to change
|
|
pfdin->pv = pfdi->pvUser; // pass callback context
|
|
pfdin->setID = setID; // required setID
|
|
pfdin->iCabinet = iCabinet; // required iCabinet
|
|
pfdin->fdie = FDIERROR_NONE; // No error
|
|
|
|
//** Get continuation cabinet
|
|
do {
|
|
fWrongCabinet = FALSE; // Assume we will get the right cabinet
|
|
|
|
//** Make sure cabinet file handles are closed
|
|
if (((pfdi->hfCabData != -1) && pfdi->pfnclose(pfdi->hfCabData)) ||
|
|
((pfdi->hfCabFiles != -1) && pfdi->pfnclose(pfdi->hfCabFiles))) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0);
|
|
return FALSE; // couldn't close old cabinet
|
|
}
|
|
//** Remember they are closed
|
|
pfdi->hfCabFiles = -1;
|
|
pfdi->hfCabData = -1;
|
|
|
|
//** Ask client for next cabinet
|
|
if (pfdi->pfnfdin(fdintNEXT_CABINET,pfdin) == -1) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0);
|
|
return FALSE; // Client aborted
|
|
}
|
|
|
|
//** Open next cabinet
|
|
if ((!LoginCabinet(pfdi,pfdi->achCabinetNext,setID,iCabinet))
|
|
|| (!SeekFolder(pfdi,0))) { // select following folder
|
|
//** Don't bail unless explicitly told to
|
|
if (pfdi->perf->erfOper == FDIERROR_USER_ABORT) {
|
|
return FALSE; // error already filled in
|
|
}
|
|
//** Have to call fdintNEXT_CABINET again
|
|
fWrongCabinet = TRUE;
|
|
}
|
|
|
|
//** Pass error code to fdintNEXT_CABINET (if we call again)
|
|
pfdin->fdie = pfdi->perf->erfOper;
|
|
}
|
|
while (fWrongCabinet); // Keep going until we get right one
|
|
|
|
//** Skip over CFFILE entries that are dups from previous cabinet
|
|
pfdi->cFilesSkipped++; // skip file we're doing right now, too
|
|
while (pfdi->cFilesSkipped) {
|
|
pfdi->cFilesRemain--;
|
|
pfdi->cFilesSkipped--;
|
|
if (!FDIReadCFFILEEntry(pfdi)) {
|
|
return FALSE; // error code already filled in
|
|
}
|
|
}
|
|
|
|
pfdi->fInContin = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** doCabinetInfoNotify - pass back info on next cabinet to client
|
|
*
|
|
* Entry:
|
|
* pfdi - FDI context pointer
|
|
*
|
|
* Exit-Success:
|
|
* returns TRUE
|
|
*
|
|
* Exit-Failure:
|
|
* returns FALSE, error filled in
|
|
*/
|
|
BOOL doCabinetInfoNotify(PFDI pfdi)
|
|
{
|
|
PFDIDECRYPT pfdid;
|
|
PFDINOTIFICATION pfdin;
|
|
|
|
//** Set info for next cabinet and pass back to client
|
|
AssertFDI(pfdi);
|
|
pfdin = &pfdi->fdin;
|
|
pfdid = &pfdi->fdid;
|
|
|
|
//** Notify extract code
|
|
pfdin->psz1 = pfdi->achCabinetNext; // pass name of next cab
|
|
pfdin->psz2 = pfdi->achDiskNext; // pass name of next disk
|
|
pfdin->psz3 = pfdi->szCabPath; // cabinet filespec
|
|
pfdin->pv = pfdi->pvUser; // pass callback context
|
|
pfdin->setID = pfdi->cfheader.setID;
|
|
pfdin->iCabinet = pfdi->cfheader.iCabinet;
|
|
if (pfdi->pfnfdin(fdintCABINET_INFO,pfdin) == -1) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0);
|
|
return FALSE; // user aborted
|
|
}
|
|
|
|
//** Notify decrypt code, if callback was supplied
|
|
if (pfdi->pfnfdid != NULL) {
|
|
pfdid->fdidt = fdidtNEW_CABINET;
|
|
pfdid->pvUser = pfdi->pvUser;
|
|
pfdid->cabinet.pHeaderReserve = pfdi->pbCFHeaderReserve;
|
|
pfdid->cabinet.cbHeaderReserve = pfdi->cbCFHeaderReserve;
|
|
pfdid->cabinet.setID = pfdi->cfheader.setID;
|
|
pfdid->cabinet.iCabinet = pfdi->cfheader.iCabinet;
|
|
if (pfdi->pfnfdid(pfdid) == -1) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0);
|
|
return FALSE; // user aborted
|
|
}
|
|
}
|
|
return TRUE;
|
|
} /* doCabinetInfoNotify() */
|
|
|
|
|
|
/*** InitFolder - make sure desired folder is ready to read
|
|
*
|
|
* Entry:
|
|
* pfdi -> general context pointer
|
|
* iFolder = iFolder field from CFFILE entry
|
|
* if iFolder == -1, then this is a continuation of prev. folder
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE, error code filled in
|
|
*
|
|
* Note:
|
|
* If we're in a continuation cabinet, this function will just
|
|
* set iFolder=0 and return. All folder initialization of future
|
|
* folders (in continuation cabinets) requires slightly different
|
|
* logic and is done elsewhere.
|
|
*/
|
|
BOOL InitFolder(PFDI pfdi,UINT iFolder)
|
|
{
|
|
if (pfdi->fInContin) {
|
|
iFolder = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
if (IS_CONTD_FORWARD(iFolder)) {
|
|
iFolder = pfdi->cfheader.cFolders-1;
|
|
}
|
|
|
|
if (pfdi->iFolder != iFolder) {
|
|
if ((!MDIResetDecompressionGlobal(pfdi))
|
|
|| (!SeekFolder(pfdi,iFolder))) {
|
|
return FALSE; // error already filled in
|
|
}
|
|
|
|
if (!FDIGetDataBlock(pfdi)) { // get the first data block into buffer
|
|
return FALSE; // bail if error
|
|
}
|
|
//** Start at offset zero in uncompressed space
|
|
pfdi->uoffFolder = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** SeekFolder - Seek open cabinet to iFolder, prepare to read data
|
|
*
|
|
* Entry:
|
|
* pfdi - FDI context
|
|
* iFolder - Folder number to open
|
|
*
|
|
* Exit-success:
|
|
* return TRUE; pfdi->pcffolder filled in; file position updated
|
|
*
|
|
* Exit-failure:
|
|
* return FALSE, error code filled in
|
|
*/
|
|
BOOL SeekFolder(PFDI pfdi,UINT iFolder)
|
|
{
|
|
PFDIDECRYPT pfdid;
|
|
|
|
AssertFDI(pfdi);
|
|
pfdid = &pfdi->fdid;
|
|
pfdi->iFolder = iFolder;
|
|
|
|
//** Read folder and position file pointer to first CFFOLDER block
|
|
if ((-1L == pfdi->pfnseek(pfdi->hfCabData,
|
|
pfdi->coffFolders +
|
|
iFolder*pfdi->cbCFFolderPlusReserve,
|
|
SEEK_SET)) // seek to CFFOLDER entry
|
|
|
|
//** Read CFFOLDER + reserved area
|
|
|| (pfdi->cbCFFolderPlusReserve !=
|
|
(UINT)pfdi->pfnread(pfdi->hfCabData,
|
|
pfdi->pcffolder,
|
|
pfdi->cbCFFolderPlusReserve))
|
|
|
|
//** Seek to first CFDATA of this folder
|
|
|| (-1L == pfdi->pfnseek(pfdi->hfCabData,
|
|
pfdi->pcffolder->coffCabStart,
|
|
SEEK_SET))) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0);
|
|
return FALSE; // problem accessing cabinet file
|
|
}
|
|
|
|
pfdi->cCFDataRemain = pfdi->pcffolder->cCFData;
|
|
|
|
#ifdef BIT16
|
|
//** 09-Jun-1994 bens Turned on Quantum library!
|
|
// #define NO_QUANTUM_16 1
|
|
#endif
|
|
|
|
if (!SetDecompressionType(pfdi->pcffolder->typeCompress,pfdi)) {
|
|
return FALSE; // error already filled in
|
|
}
|
|
|
|
//** Notify decrypt code, if callback was supplied
|
|
if (pfdi->pfnfdid != NULL) {
|
|
pfdid->fdidt = fdidtNEW_FOLDER;
|
|
pfdid->pvUser = pfdi->pvUser;
|
|
//** Point to per folder reserved area
|
|
Assert(pfdi->cbCFFolderPlusReserve >= sizeof(CFFOLDER));
|
|
pfdid->folder.cbFolderReserve = pfdi->cbCFFolderPlusReserve
|
|
- sizeof(CFFOLDER);
|
|
if (pfdid->folder.cbFolderReserve > 0) {
|
|
pfdid->folder.pFolderReserve = ((BYTE *)pfdi->pcffolder)
|
|
+ sizeof(CFFOLDER);
|
|
}
|
|
else {
|
|
pfdid->folder.pFolderReserve = NULL; // No reserved data
|
|
}
|
|
pfdid->folder.iFolder = iFolder;
|
|
if (pfdi->pfnfdid(pfdid) == -1) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0);
|
|
return FALSE; // user aborted
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** FDIReadCFFILEEntry -- Read in a complete CFFILE entry
|
|
*
|
|
* Entry:
|
|
* pfdi -- pointer to FDI context
|
|
*
|
|
* Exit success:
|
|
* pfdi->cffile structure filled in
|
|
* returns TRUE
|
|
*
|
|
* Exit failure:
|
|
* returns FALSE if couldn't read a CFFILE entry, perf filled in
|
|
*
|
|
* Note:
|
|
* unlike in this routine's counterpart in FCI, it is a fatal
|
|
* error if we hit an eof, because at this point, we never call
|
|
* this routine unless we know it can expect a valid entry
|
|
*/
|
|
BOOL FDIReadCFFILEEntry(PFDI pfdi)
|
|
{
|
|
if ((sizeof(CFFILE) != (unsigned) pfdi->pfnread(pfdi->hfCabFiles,
|
|
&pfdi->cffile,
|
|
sizeof (CFFILE)))
|
|
|| !FDIReadPSZ(pfdi->achName,
|
|
CB_MAX_FILENAME,
|
|
pfdi))
|
|
{
|
|
ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0);
|
|
return FALSE; // couldn't get a full valid CFFILE record
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** FDIReadCFDATAEntry - Read in a complete CFDATA entry
|
|
*
|
|
* Entry:
|
|
* pfdi - Pointer to FDI context (for file i/o)
|
|
* cbPartial - Amount of data already present in our local data
|
|
* buffer (pfdi->pchCompr). 0 means we haven't
|
|
* read any data yet for this block; greater than
|
|
* 0 means we've read the first portion of a split
|
|
* block, and we're reading the second piece now.
|
|
*
|
|
* Exit-Success:
|
|
* cfdata structure filled in
|
|
* return code TRUE
|
|
*
|
|
* Exit-Failure:
|
|
* return code FALSE, error structure filled in
|
|
*
|
|
* Notes:
|
|
* Unlike this routine's counterpart in the FCI stuff, it is a fatal
|
|
* error here if we get an EOF. That is because FDI knows for sure
|
|
* how many entries there should be and never tries to read too much.
|
|
*/
|
|
int FDIReadCFDATAEntry(PFDI pfdi, UINT cbPartial)
|
|
{
|
|
BOOL fSplit; // TRUE if this is read of the first
|
|
// or second piece of a split block!
|
|
PFDIDECRYPT pfdid;
|
|
CHECKSUM calcsum;
|
|
|
|
AssertFDI(pfdi);
|
|
pfdid = &pfdi->fdid;
|
|
|
|
//** Read CFDATA plus reserved section
|
|
if ((pfdi->cbCFDataPlusReserve != pfdi->pfnread(pfdi->hfCabData,
|
|
pfdi->pcfdata,
|
|
pfdi->cbCFDataPlusReserve))
|
|
|
|
//** Make sure amount read is not larger than compressed data buffer
|
|
|| ((pfdi->pcfdata->cbData + cbPartial) > pfdi->cbMaxCompr)
|
|
|
|
//** Read actual data itself
|
|
|| ((pfdi->pcfdata->cbData != pfdi->pfnread(pfdi->hfCabData,
|
|
&pfdi->pchCompr[cbPartial],
|
|
pfdi->pcfdata->cbData)))) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0);
|
|
return FALSE; // no valid record available
|
|
}
|
|
|
|
|
|
//** JEFFWE - Check CRC if it is set
|
|
if (pfdi->pcfdata->csum != 0) {
|
|
calcsum = CSUMCompute(&(pfdi->pcfdata->cbData),
|
|
pfdi->cbCFDataPlusReserve - sizeof(CHECKSUM),
|
|
CSUMCompute(&(pfdi->pchCompr[cbPartial]),
|
|
pfdi->pcfdata->cbData,
|
|
0
|
|
)
|
|
);
|
|
if (calcsum != pfdi->pcfdata->csum) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
pfdi->pcfdata->cbData += cbPartial; // make it look like one whole block
|
|
|
|
//** Determine if this is a split data block
|
|
fSplit = (cbPartial > 0) // Second piece of split block
|
|
|| (pfdi->pcfdata->cbUncomp == 0); // First piece of split block
|
|
|
|
//** Notify decrypt code, if callback was supplied
|
|
if (pfdi->pfnfdid != NULL) {
|
|
pfdid->fdidt = fdidtDECRYPT;
|
|
pfdid->pvUser = pfdi->pvUser;
|
|
//** Point to per data block reserved area
|
|
Assert(pfdi->cbCFDataPlusReserve >= sizeof(CFDATA));
|
|
pfdid->decrypt.cbDataReserve = pfdi->cbCFDataPlusReserve
|
|
- sizeof(CFDATA);
|
|
if (pfdid->decrypt.cbDataReserve > 0) {
|
|
pfdid->decrypt.pDataReserve = ((BYTE *)pfdi->pcfdata)
|
|
+ sizeof(CFDATA);
|
|
}
|
|
else {
|
|
pfdid->decrypt.pDataReserve = NULL; // No reserved data
|
|
}
|
|
|
|
pfdid->decrypt.pbData = &pfdi->pchCompr[cbPartial];
|
|
pfdid->decrypt.cbData = pfdi->pcfdata->cbData;
|
|
pfdid->decrypt.fSplit = fSplit;
|
|
pfdid->decrypt.cbPartial = cbPartial;
|
|
if (pfdi->pfnfdid(pfdid) == -1) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_USER_ABORT,0);
|
|
return FALSE; // user aborted
|
|
}
|
|
}
|
|
return TRUE;
|
|
} /* FDIReadCFDATAEntry() */
|
|
|
|
|
|
/*** FDIReadPSZ -- Read in a psz name
|
|
*
|
|
* Entry:
|
|
* pb - buffer to load name into
|
|
* cb - maximum legal length for name
|
|
* pfdi - pointer to FDI context (for file i/o functions)
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE, name filled in
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE (file read error, or string too long)
|
|
*/
|
|
BOOL FDIReadPSZ(char *pb, int cb, PFDI pfdi)
|
|
{
|
|
char chLast;
|
|
int cbValue;
|
|
int cbRead;
|
|
long pos;
|
|
|
|
//** Save current position
|
|
pos = (*pfdi->pfnseek)(pfdi->hfCabFiles,0,SEEK_CUR);
|
|
|
|
//** Read in enough to get longest possible value
|
|
cbRead = (*pfdi->pfnread)(pfdi->hfCabFiles,pb,cb);
|
|
if (cbRead <= 0) { // At EOF, or an error occured
|
|
ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Pick out just ASCIIZ string and adjust file position
|
|
chLast = pb[cb-1]; // Save last character
|
|
pb[cb-1] = '\0'; // Ensure terminated
|
|
cbValue = strlen(pb); // Get string length
|
|
if ( ((cbValue+1) >= cb) && (chLast != '\0')) {
|
|
//** String filled up buffer and was not null terminated in
|
|
// file, so something must be wrong.
|
|
ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Position to just past string
|
|
if (-1L == (*pfdi->pfnseek)(pfdi->hfCabFiles,pos+cbValue+1,SEEK_SET)) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_CORRUPT_CABINET,0);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** SetDecompressionType -- initializes a new decompressor
|
|
*
|
|
* Entry:
|
|
* typeCompress -- new compression type (tcompBAD to term w/ no new)
|
|
* pfdi -- FDI context structure
|
|
*
|
|
* Exit-success:
|
|
* returns TRUE;
|
|
*
|
|
* Exit-failure:
|
|
* returns FALSE, error code filled in
|
|
*/
|
|
BOOL SetDecompressionType(TCOMP typeCompress,PFDI pfdi)
|
|
{
|
|
//** Don't do anything if type is unchanged
|
|
if (typeCompress == pfdi->typeCompress) {
|
|
return TRUE;
|
|
}
|
|
|
|
//** Destroy existing decompression context (if any)
|
|
if (!MDIDestroyDecompressionGlobal(pfdi)) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0);
|
|
return FALSE;
|
|
}
|
|
|
|
//** Create new decompression context
|
|
pfdi->typeCompress = typeCompress;
|
|
if (!MDICreateDecompressionGlobal(pfdi))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** MDIDestroyDecompressionGlobal -- Destroy the currently selected decompressor
|
|
*
|
|
* Entry:
|
|
* pfdi - pointer to FDI context
|
|
*
|
|
* Exit-success:
|
|
* returns TRUE
|
|
*
|
|
* Exit-failure:
|
|
* returns FALSE, error code filled in
|
|
*/
|
|
BOOL MDIDestroyDecompressionGlobal(PFDI pfdi)
|
|
{
|
|
switch(CompressionTypeFromTCOMP(pfdi->typeCompress)) {
|
|
|
|
case tcompBAD: // no existing compression
|
|
return TRUE; // nothing to do if there wasn't any compressor
|
|
|
|
case tcompTYPE_NONE:
|
|
break; //no action needed for null compressor
|
|
|
|
case tcompTYPE_MSZIP:
|
|
if (MDI_ERROR_NO_ERROR != MDIDestroyDecompression(pfdi->mdh)) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0);
|
|
return FALSE; // no valid compressor initialized
|
|
}
|
|
break;
|
|
|
|
#if !defined(BIT16) || !defined(NO_QUANTUM_16)
|
|
case tcompTYPE_QUANTUM:
|
|
if (MDI_ERROR_NO_ERROR != QDIDestroyDecompression(pfdi->mdh)) {
|
|
ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0);
|
|
return FALSE; // no valid compressor initialized
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
ErfSetCodes(pfdi->perf,FDIERROR_BAD_COMPR_TYPE,0);
|
|
return FALSE;
|
|
} // end switch
|
|
|
|
//** Now free the buffers
|
|
pfdi->pfnfree (pfdi->pchCompr);
|
|
pfdi->pfnfree (pfdi->pchUncompr);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** MDICreateDecompressionGlobal -- Create the currently selected decompressor
|
|
*
|
|
* Entry:
|
|
* pfdi -- pointer to FDI context
|
|
*
|
|
* Exit-success:
|
|
* returns TRUE
|
|
*
|
|
* Exit-failure:
|
|
* returns FALSE, error code filled in
|
|
*
|
|
*/
|
|
BOOL MDICreateDecompressionGlobal(PFDI pfdi)
|
|
{
|
|
QUANTUMDECOMPRESS qdec;
|
|
FDIERROR fdierror = FDIERROR_NONE;
|
|
int mdierror;
|
|
|
|
pfdi->cbMaxUncompr = CB_MAX_CHUNK;
|
|
|
|
/* first pass to establish buffer sizes */
|
|
|
|
switch(CompressionTypeFromTCOMP(pfdi->typeCompress)) {
|
|
|
|
case tcompBAD: // no new compressor
|
|
return TRUE; // all done if no compressor enabled
|
|
|
|
case tcompTYPE_NONE:
|
|
pfdi->cbMaxCompr = pfdi->cbMaxUncompr; // for null compr., bufs are same
|
|
break;
|
|
|
|
case tcompTYPE_MSZIP:
|
|
if (MDI_ERROR_NO_ERROR != MDICreateDecompression(&pfdi->cbMaxUncompr,
|
|
NULL,
|
|
NULL,
|
|
&pfdi->cbMaxCompr,
|
|
NULL))
|
|
{
|
|
fdierror = FDIERROR_MDI_FAIL;
|
|
}
|
|
break;
|
|
|
|
#if !defined(BIT16) || !defined(NO_QUANTUM_16)
|
|
case tcompTYPE_QUANTUM:
|
|
qdec.WindowBits = CompressionMemoryFromTCOMP(pfdi->typeCompress);
|
|
|
|
//** Set CPU type, make sure FDI & QDI definitions match
|
|
Assert(QDI_CPU_UNKNOWN == cpuUNKNOWN);
|
|
Assert(QDI_CPU_80286 == cpu80286);
|
|
Assert(QDI_CPU_80386 == cpu80386);
|
|
qdec.fCPUtype = pfdi->cpuType;
|
|
|
|
if (MDI_ERROR_NO_ERROR != QDICreateDecompression(&pfdi->cbMaxUncompr,
|
|
&qdec,
|
|
NULL,
|
|
NULL,
|
|
&pfdi->cbMaxCompr,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL))
|
|
{
|
|
fdierror = FDIERROR_MDI_FAIL;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
fdierror = FDIERROR_BAD_COMPR_TYPE;
|
|
}
|
|
|
|
if (fdierror != FDIERROR_NONE)
|
|
{
|
|
ErfSetCodes(pfdi->perf,fdierror,0);
|
|
pfdi->typeCompress = tcompBAD; // No compression context
|
|
return FALSE;
|
|
}
|
|
|
|
//** Now allocate whatever buffers the selected compressor requested
|
|
|
|
if (NULL == (pfdi->pchCompr = (char *) pfdi->pfnalloc (pfdi->cbMaxCompr)))
|
|
{
|
|
ErfSetCodes(pfdi->perf,FDIERROR_ALLOC_FAIL,0);
|
|
pfdi->typeCompress = tcompBAD; // No compression context
|
|
return FALSE;
|
|
}
|
|
|
|
if (NULL == (pfdi->pchUncompr = (char *) pfdi->pfnalloc (pfdi->cbMaxUncompr)))
|
|
{
|
|
pfdi->pfnfree(pfdi->pchCompr);
|
|
ErfSetCodes(pfdi->perf,FDIERROR_ALLOC_FAIL,0);
|
|
pfdi->typeCompress = tcompBAD; // No compression context
|
|
return FALSE;
|
|
}
|
|
|
|
/* second pass to really setup a decompressor */
|
|
|
|
switch(CompressionTypeFromTCOMP(pfdi->typeCompress)) {
|
|
|
|
case tcompTYPE_MSZIP:
|
|
mdierror = MDICreateDecompression(&pfdi->cbMaxUncompr,
|
|
pfdi->pfnalloc,
|
|
pfdi->pfnfree,
|
|
&pfdi->cbMaxCompr,
|
|
&pfdi->mdh);
|
|
if (mdierror != MDI_ERROR_NO_ERROR)
|
|
{
|
|
if (mdierror == MDI_ERROR_NOT_ENOUGH_MEMORY)
|
|
{
|
|
fdierror = FDIERROR_ALLOC_FAIL;
|
|
}
|
|
else
|
|
{
|
|
fdierror = FDIERROR_MDI_FAIL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
#if !defined(BIT16) || !defined(NO_QUANTUM_16)
|
|
case tcompTYPE_QUANTUM:
|
|
mdierror = QDICreateDecompression(&pfdi->cbMaxUncompr,
|
|
&qdec,
|
|
pfdi->pfnalloc,
|
|
pfdi->pfnfree,
|
|
&pfdi->cbMaxCompr,
|
|
&pfdi->mdh,
|
|
pfdi->pfnopen,
|
|
pfdi->pfnread,
|
|
pfdi->pfnwrite,
|
|
pfdi->pfnclose,
|
|
pfdi->pfnseek);
|
|
if (mdierror != MDI_ERROR_NO_ERROR)
|
|
{
|
|
if (mdierror == MDI_ERROR_NOT_ENOUGH_MEMORY)
|
|
{
|
|
fdierror = FDIERROR_ALLOC_FAIL;
|
|
}
|
|
else
|
|
{
|
|
fdierror = FDIERROR_MDI_FAIL;
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
if (fdierror != FDIERROR_NONE)
|
|
{
|
|
pfdi->pfnfree(pfdi->pchCompr);
|
|
pfdi->pfnfree(pfdi->pchUncompr);
|
|
ErfSetCodes(pfdi->perf,fdierror,0);
|
|
pfdi->typeCompress = tcompBAD; // No compression context
|
|
return FALSE;
|
|
}
|
|
|
|
// NOTE: At this point, we leave the compression type set in pfdi,
|
|
// so that SetDecompressionType() will clean up the handle
|
|
// to the compression context.
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** MDIResetDecompressionGlobal -- reset the currently selected decompressor
|
|
*
|
|
* Entry:
|
|
* pfdi -- pointer to folder context
|
|
*
|
|
* Exit-success:
|
|
* returns TRUE
|
|
*
|
|
* Exit-failure:
|
|
* returns FALSE, error code filled in
|
|
*/
|
|
BOOL MDIResetDecompressionGlobal(PFDI pfdi)
|
|
{
|
|
switch(CompressionTypeFromTCOMP(pfdi->typeCompress)) {
|
|
|
|
case tcompBAD:
|
|
break; // no compression selected
|
|
|
|
case tcompTYPE_NONE:
|
|
break; // no action for null compressor
|
|
|
|
case tcompTYPE_MSZIP:
|
|
if (MDI_ERROR_NO_ERROR != MDIResetDecompression(pfdi->mdh))
|
|
{
|
|
ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0);
|
|
return FALSE; // no valid compressor initialized
|
|
}
|
|
break;
|
|
|
|
#if !defined(BIT16) || !defined(NO_QUANTUM_16)
|
|
case tcompTYPE_QUANTUM:
|
|
if (MDI_ERROR_NO_ERROR != QDIResetDecompression(pfdi->mdh))
|
|
{
|
|
ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0);
|
|
return FALSE; // no valid compressor initialized
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
ErfSetCodes(pfdi->perf,FDIERROR_BAD_COMPR_TYPE,0);
|
|
return FALSE; // unknown compression type
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*** MDIDecompressGlobal - Decompress using currently selected decompressor
|
|
*
|
|
* Entry:
|
|
* pfdi - Pointer to FDI context
|
|
* pcbData - Location to return the compressed size;
|
|
* NOTE: For Quantum, must contain the EXACT EXPECTED
|
|
* UNCOMPRESSED data size!
|
|
*
|
|
* Exit-Success:
|
|
* Returns TRUE
|
|
*
|
|
* Exit-Failure:
|
|
* Returns FALSE, error structure filled in
|
|
*/
|
|
BOOL MDIDecompressGlobal(PFDI pfdi, USHORT *pcbData)
|
|
{
|
|
UINT cbData;
|
|
|
|
switch(CompressionTypeFromTCOMP(pfdi->typeCompress)) {
|
|
case tcompTYPE_NONE:
|
|
memcpy(pfdi->pchUncompr,
|
|
pfdi->pchCompr,
|
|
*pcbData=pfdi->pcfdata->cbData);
|
|
break; // done for null compressor
|
|
|
|
case tcompTYPE_MSZIP:
|
|
cbData = pfdi->cbMaxUncompr; // Size of destination buffer
|
|
if (MDI_ERROR_NO_ERROR !=
|
|
MDIDecompress(pfdi->mdh, // MDI context
|
|
pfdi->pchCompr, // Compressed data
|
|
pfdi->pcfdata->cbData, // source buffer size
|
|
pfdi->pchUncompr, // Destination buffer
|
|
&cbData)) { // resulting data size
|
|
ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0);
|
|
return FALSE;
|
|
}
|
|
//** Narrow result (16-bit case)
|
|
*pcbData = (USHORT)cbData;
|
|
break;
|
|
|
|
#if !defined(BIT16) || !defined(NO_QUANTUM_16)
|
|
case tcompTYPE_QUANTUM:
|
|
cbData = (UINT)*pcbData; // Size of *uncompressed* data!
|
|
if (MDI_ERROR_NO_ERROR !=
|
|
QDIDecompress(pfdi->mdh, // QDI context
|
|
pfdi->pchCompr, // Compressed data
|
|
pfdi->pcfdata->cbData, // source buffer size
|
|
pfdi->pchUncompr, // Destination buffer
|
|
&cbData)) { // resulting data size
|
|
ErfSetCodes(pfdi->perf,FDIERROR_MDI_FAIL,0);
|
|
return FALSE;
|
|
}
|
|
//** Narrow result (16-bit case)
|
|
*pcbData = (USHORT)cbData;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
ErfSetCodes(pfdi->perf,FDIERROR_BAD_COMPR_TYPE,0);
|
|
return FALSE; // no valid compressor initialized
|
|
}
|
|
return TRUE;
|
|
}
|