/*** 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 #include #include #include #include #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; }