/*** ddump.c - Diamond cabinet file dumper * * Microsoft Confidential * Copyright (C) Microsoft Corporation 1994 * All Rights Reserved. * * Author: * Benjamin W. Slivka * * History: * 16-Mar-1994 bens Initial version (started with extract.c) * 28-Mar-1994 bens Update for version 1.03. * 18-May-1994 bens Correct order of cabinet file name, disk name */ #include #include #include #include #include #include #include #include #include #include #include #include "types.h" #include "asrt.h" #include "error.h" #include "mem.h" #include "message.h" #include "filelist.h" #include "fileutil.h" #include "chuck\types.h" #include "chuck\cabinet.h" #include "ddump.msg" //** Constants #define cbMAX_LINE 256 // Maximum output line length //** Types typedef enum { actBAD, // Invalid action actHELP, // Show help actFILE, // Extract from single file cabinet(s) actCABINET, // Extract from multiple-file cabinet(s) } ACTION; /* act */ #ifdef ASSERT #define sigSESSION MAKESIG('S','E','S','S') // SESSION signature #define AssertSess(psess) AssertStructure(psess,sigSESSION); #else // !ASSERT #define AssertSess(psess) #endif // !ASSERT typedef struct { #ifdef ASSERT SIGNATURE sig; // structure signature sigSESSION #endif ACTION act; // Action to perform HFILELIST hflist; // List of files specified on cmd line BOOL fNoLineFeed; // TRUE if last printf did not have \n BOOL fVerbose; // TRUE ==> Dump everything long cFiles; // Total files processed PERROR perr; // Pass through FDI char achMsg[cbMAX_LINE*2]; // Message formatting buffer char achFile[cbFILE_NAME_MAX]; // Current filename being extracted char achPrevDisk[255]; char achPrevCabFile[255]; char achNextDisk[255]; char achNextCabFile[255]; CFHEADER cfheader; CFRESERVE cfreserve; char *pbCFHeaderReserve; // Reserved data for cabinet CFFOLDER *pcffolder; // CFFOLDER + reserved buffer CFDATA *pcfdata; // CFDATA + reserved buffer UINT cbCFHeaderReserve; // Size of CFHEADER RESERVE UINT cbCFFolderPlusReserve; // Size of CFFOLDER + folder RESERVE UINT cbCFDataPlusReserve; // Size of CFDATA + data RESERVE } SESSION; /* sess */ typedef SESSION *PSESSION; /* psess */ //** Function Prototypes FNASSERTFAILURE(fnafReport); HFILESPEC addFileSpec(PSESSION psess,char *pszArg,PERROR perr); BOOL doCabinet(PSESSION psess,PERROR perr); BOOL parseCommandLine(PSESSION psess,int cArg,char *apszArg[],PERROR perr); void printError(PSESSION psess,PERROR perr); void pszFromMSDOSTime(char *psz,int cb,WORD date,WORD time); BOOL doCFHeader(PSESSION psess,int fh,char *pszFile,PERROR perr); BOOL doCFFolder(PSESSION psess,int fh,int iFolder,PERROR perr); BOOL doCFFile(PSESSION psess,int fh,int iFile,PERROR perr); ULONG doCFData(PSESSION psess,int fh,int iData,ULONG uoffFolder,PERROR perr); BOOL readPSZ(int fh, char *pb, int cb); //** Functions /*** main - DDUMP main program * * NOTE: We're sloppy, and don't free resources allocated by * functions we call, on the assumption that program exit * will clean up memory and file handles for us. */ int __cdecl main(int cArg, char *apszArg[]) { char ach[cbMSG_MAX]; ERROR err; PSESSION psess; AssertRegisterFunc(fnafReport); // Register assertion reporter ErrClear(&err); // No error err.pszFile = NULL; // No file being processed, yet //** Initialize session psess = MemAlloc(sizeof(SESSION)); if (!psess) { ErrSet(&err,pszDDERR_NO_SESSION); printError(psess,&err); exit(1); } SetAssertSignature((psess),sigSESSION); psess->hflist = NULL; psess->fNoLineFeed = FALSE; // No print status, yet psess->fVerbose = FALSE; // Default to non-verbose output psess->cFiles = 0; // No files, yet psess->pbCFHeaderReserve = NULL; // No per-cabinet reserved data, yet psess->pcffolder = NULL; // No buffer for CFFOLDER, yet psess->pcfdata = NULL; // No buffer for CFDATA, yet //** Print Extract banner MsgSet(ach,pszBANNER,"%s",pszDDUMP_VERSION); printf(ach); //** Parse command line if (!parseCommandLine(psess,cArg,apszArg,&err)) { printError(psess,&err); return 1; } //** Quick out if command line help is requested if (psess->act == actHELP) { // Do help if any args, for now printf("\n"); // Separate banner from help printf(pszCMD_LINE_HELP); return 0; } //** Have some work to do -- go do it if (!doCabinet(psess,&err)) { printError(psess,&err); return 1; } //** Free resources AssertSess(psess); ClearAssertSignature((psess)); MemFree(psess); //** Success return 0; } /* main */ /*** doCabinet - Dump contents of cabinet * * Entry: * psess - Description of operation to perform * perr - ERROR structure * * Exit-Success: * Returns TRUE; directory displayed * * Exit-Failure: * Returns FALSE; perr filled in with details. */ BOOL doCabinet(PSESSION psess, PERROR perr) { UINT cCFData; int fh; // File handle of cabinet HFILESPEC hfspec; UINT i; ULONG off; ULONG offCFDataForFirstFolder; char *pszCabinet; // Cabinet filespec ULONG uoff; ULONG uoffFolder; //** Process a cabinet hfspec = FLFirstFile(psess->hflist); Assert(hfspec != NULL); // Must have at least one file pszCabinet = FLGetSource(hfspec); Assert(pszCabinet!=NULL); Assert(*pszCabinet); //** Open cabinet file if (-1 == (fh = _open(pszCabinet,_O_BINARY | _O_RDONLY))) { ErrSet(perr,pszDDERR_OPEN_FAILED,"%s",pszCabinet); return FALSE; } printf("Cabinet File: %s\n",pszCabinet); //** Do header if (!doCFHeader(psess,fh,pszCabinet,perr)) { return FALSE; } //** Do folders cCFData = 0; for (i=0; icfheader.cFolders; i++) { if (!doCFFolder(psess,fh,i,perr)) { return FALSE; } cCFData += psess->pcffolder->cCFData; // Count total CFData's if (i == 0) { offCFDataForFirstFolder = psess->pcffolder->coffCabStart; } } //** Make sure CFFILEs start where header said they did off = _lseek(fh,0,SEEK_CUR); if (psess->cfheader.coffFiles != off) { printf("ERROR: Header says CFFILES start at %ld, really start at %ld!\n", psess->cfheader.coffFiles, off); } //** Do files for (i=0; icfheader.cFiles; i++) { if (!doCFFile(psess,fh,i,perr)) { return FALSE; } } //** Make sure CFDATAs start where first CFFOLDER said they did off = _lseek(fh,0,SEEK_CUR); if (offCFDataForFirstFolder != off) { printf("ERROR: CFFOLDER says CFDATA start at %ld, really start at %ld!\n", offCFDataForFirstFolder, off); } //** Only traverse through CFDATAs if requested (speeds up dumping on // floppy disks, since we don't have to read the entire floppy! if (psess->fVerbose) { //** Do data blocks uoffFolder = 0; // Track uncompressed offset for (i=0; icfheader.cbCabinet != (long)off) { printf("ERROR: Header says cabinet is %ld bytes, really is %ld!\n", psess->cfheader.cbCabinet, off); } } //** Success return TRUE; } /* doCabinet() */ /*** doCFHeader - display CFHEADER information * */ BOOL doCFHeader(PSESSION psess, int fh, char *pszFile, PERROR perr) { BOOL fPrev=FALSE; BOOL fNext=FALSE; BOOL fReserve=FALSE; //** Read CFHEADER structure if (sizeof(CFHEADER) != _read(fh,&psess->cfheader,sizeof(CFHEADER))) { ErrSet(perr,pszDDERR_NOT_A_CABINET,"%s",pszFile); return FALSE; } //** Make sure this is a cabinet file if (psess->cfheader.sig != sigCFHEADER) { ErrSet(perr,pszDDERR_NOT_A_CABINET,"%s",pszFile); return FALSE; // Signature does not match } //** Make sure we know this version number if (psess->cfheader.version != verCF) { ErrSet(perr,pszDDERR_UNKNOWN_CABINET_VERSION,"%s%d", pszFile,psess->cfheader.version); return FALSE; } psess->achMsg[0] = '\0'; if (psess->cfheader.flags & cfhdrPREV_CABINET) { strcat(psess->achMsg,"Previous "); fPrev = TRUE; } if (psess->cfheader.flags & cfhdrNEXT_CABINET) { strcat(psess->achMsg,"Next "); fNext = TRUE; } if (psess->cfheader.flags & cfhdrRESERVE_PRESENT) { strcat(psess->achMsg,"Reserve "); fReserve = TRUE; } printf("sig: %08lx\n" ,psess->cfheader.sig); printf("csumHeader: %08lx\n" ,psess->cfheader.csumHeader); printf("cbCabinet: %8ld\n" ,psess->cfheader.cbCabinet); printf("csumFolders: %08lx\n" ,psess->cfheader.csumFolders); printf("coffFiles: %8ld\n" ,psess->cfheader.coffFiles); printf("csumFiles: %08lx\n" ,psess->cfheader.csumFiles); printf("version: %04x\n" ,psess->cfheader.version); printf("cFolders: %8d\n" ,psess->cfheader.cFolders); printf("cFiles: %8d\n" ,psess->cfheader.cFiles); printf("flags: %04x (%s)\n",psess->cfheader.flags,psess->achMsg); printf("setID: %04x\n" ,psess->cfheader.setID); printf("iCabinet: %05d\n" ,psess->cfheader.iCabinet); //** Assume no CFRESERVE psess->cbCFHeaderReserve = 0; psess->cbCFFolderPlusReserve = sizeof(CFFOLDER); psess->cbCFDataPlusReserve = sizeof(CFDATA); //** Read CFRESERVE, if present if (fReserve) { //** Read CFRESERVE structure if (sizeof(CFRESERVE) != _read(fh,&psess->cfreserve,sizeof(CFRESERVE))) { return FALSE; } if (psess->pbCFHeaderReserve) { MemFree(psess->pbCFHeaderReserve); psess->pbCFHeaderReserve = NULL; } if (!(psess->pbCFHeaderReserve = MemAlloc(psess->cfreserve.cbCFHeader))) { return FALSE; } psess->cbCFHeaderReserve = psess->cfreserve.cbCFHeader; psess->cbCFFolderPlusReserve += psess->cfreserve.cbCFFolder; psess->cbCFDataPlusReserve += psess->cfreserve.cbCFData; printf(" cbCFHeader: %5d\n", psess->cfreserve.cbCFHeader); printf(" cbCFFolder: %5d\n", psess->cfreserve.cbCFFolder); printf(" cbCFData: %5d\n", psess->cfreserve.cbCFData); //** Read reserved section if (psess->cbCFHeaderReserve != (UINT)_read(fh, psess->pbCFHeaderReserve, psess->cbCFHeaderReserve)) { return FALSE; } //BUGBUG 17-Mar-1994 bens Print out reserved bytes with hex dump? } //** Allocate CFFOLDER and CFDATA buffers if (psess->pcffolder) { MemFree(psess->pcffolder); psess->pcffolder = NULL; } if (!(psess->pcffolder = MemAlloc(psess->cbCFFolderPlusReserve))) { return FALSE; } if (psess->pcfdata) { MemFree(psess->pcfdata); psess->pcfdata = NULL; } if (!(psess->pcfdata = MemAlloc(psess->cbCFDataPlusReserve))) { return FALSE; } //** Read prev/next disk/cab strings psess->achPrevCabFile[0] = '\0'; psess->achPrevDisk[0] = '\0'; psess->achNextCabFile[0] = '\0'; psess->achNextDisk[0] = '\0'; if (fPrev) { if (!readPSZ(fh,psess->achPrevCabFile,sizeof(psess->achPrevCabFile))) { return FALSE; } if (!readPSZ(fh,psess->achPrevDisk,sizeof(psess->achPrevDisk))) { return FALSE; } printf("PrevCabFile: %s\n",psess->achPrevCabFile); printf("PrevDisk: %s\n",psess->achPrevDisk); } if (fNext) { if (!readPSZ(fh,psess->achNextCabFile,sizeof(psess->achNextCabFile))) { return FALSE; } if (!readPSZ(fh,psess->achNextDisk,sizeof(psess->achNextDisk))) { return FALSE; } printf("NextCabFile: %s\n",psess->achNextCabFile); printf("NextDisk: '%s'\n",psess->achNextDisk); } //** Blank line to separate header from first folder printf("\n"); return TRUE; } /*** doCFFolderr - display CFFOLDER information * */ BOOL doCFFolder(PSESSION psess, int fh, int iFolder, PERROR perr) { char ach[256]; static BOOL fFirstTime=TRUE; char *pszCompression; //** Read CFFOLDER and reserved data if (psess->cbCFFolderPlusReserve != (UINT)_read(fh, psess->pcffolder, psess->cbCFFolderPlusReserve)) { return FALSE; } switch (CompressionTypeFromTCOMP(psess->pcffolder->typeCompress)) { case tcompTYPE_NONE: pszCompression = "None"; break; case tcompTYPE_MSZIP: pszCompression = "MSZIP"; break; case tcompTYPE_QUANTUM: sprintf(ach,"Quantum Level=%d Memory=%d", CompressionLevelFromTCOMP(psess->pcffolder->typeCompress), CompressionMemoryFromTCOMP(psess->pcffolder->typeCompress)); pszCompression = ach; break; default: sprintf(psess->achMsg,"Unknown compression: %04x", psess->pcffolder->typeCompress); pszCompression = psess->achMsg; break; } if (fFirstTime) { printf("iFolder offCabinet cData Compression\n"); fFirstTime = FALSE; } printf(" %3d %8ld %5d %s\n", iFolder, psess->pcffolder->coffCabStart, psess->pcffolder->cCFData, pszCompression); //BUGBUG 17-Mar-1994 bens Dump reserved data in hex dump form? return TRUE; } /*** doCFFile - display CFFILE information * */ BOOL doCFFile(PSESSION psess, int fh, int iFile, PERROR perr) { CFFILE cffile; static BOOL fFirstTime=TRUE; psess->cFiles++; // Count file //** Read CFFILE structure if (sizeof(CFFILE) != _read(fh,&cffile,sizeof(cffile))) { return FALSE; } if (!readPSZ(fh,psess->achFile,sizeof(psess->achFile))) { return FALSE; } //** Format date and time pszFromMSDOSTime(psess->achMsg, sizeof(psess->achMsg), cffile.date, cffile.time); if (fFirstTime) { printf("\n"); printf("iFile cbFile offFolder iFolder attr date time name\n"); fFirstTime = FALSE; } printf("%5d %8ld %9ld %7d %04x %s %s\n", iFile, cffile.cbFile, cffile.uoffFolderStart, cffile.iFolder, cffile.attribs, psess->achMsg, psess->achFile ); return TRUE; } /*** doCFData - display CFDATA information * */ ULONG doCFData(PSESSION psess, int fh, int iData, ULONG uoffFolder, PERROR perr) { static BOOL fFirstTime=TRUE; //** Read CFFOLDER and reserved data if (psess->cbCFDataPlusReserve != (UINT)_read(fh, psess->pcfdata, psess->cbCFDataPlusReserve)) { return FALSE; } if (fFirstTime) { printf("\n"); printf("iData csumData offFolder cbData cbUncomp\n"); fFirstTime = FALSE; } printf("%5d %08lx %9ld %6u %6u\n", iData, psess->pcfdata->csum, uoffFolder, psess->pcfdata->cbData, psess->pcfdata->cbUncomp); //** Skip over actual data _lseek(fh,psess->pcfdata->cbData,SEEK_CUR); return (ULONG)psess->pcfdata->cbUncomp; } /* doCFData() */ /*** readPSZ - read ASCIIZ string from file * */ BOOL readPSZ(int fh, char *pb, int cb) { char chLast; int cbValue; int cbRead; long pos; pos = _lseek(fh,0,SEEK_CUR); // Save current position //** Read in enough to get longest possible value cbRead = _read(fh,pb,cb); //BUGBUG 17-Mar-1994 bens Need to check for error/eof from read 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. return FALSE; } _lseek(fh,pos+cbValue+1,SEEK_SET); // Position to just past string return TRUE; } /*** parseCommandLine - Parse the command line arguments * * Entry: * cArg - Count of arguments, including program name * apszArg - Array of argument strings * perr - ERROR structure * * Exit-Success: * Returns TRUE; psess filled in. * * Exit-Failure: * Returns actBAD; perr filled in with error. */ BOOL parseCommandLine(PSESSION psess, int cArg, char *apszArg[], PERROR perr) { int cFile=0; // Count of non-directive file names seen char ch; // Switch value int i; AssertSess(psess); psess->act = actBAD; // We don't know what we are doing, yet //** Parse args, skipping program name for (i=1; ifVerbose = TRUE; // Show everything break; case chSWITCH_HELP: if (apszArg[i][2] != '\0') { ErrSet(perr,pszDDERR_BAD_SWITCH,"%s",apszArg[i]); return FALSE; } psess->act = actHELP; // Show help return TRUE; default: ErrSet(perr,pszDDERR_BAD_SWITCH,"%s",apszArg[i]); return FALSE; break; } } else { //** We have a file name; add it to our list if (addFileSpec(psess,apszArg[i],perr) == NULL) { ErrSet(perr,pszDDERR_COULD_NOT_ADD_FILE,"%s",apszArg[i]); return FALSE; } cFile++; // Count files } } //** Make sure we got at least one filespec if (cFile == 0) { psess->act = actHELP; // Show help } //** Success return TRUE; } /*** addFileSpec - Add filename to session list * * Entry: * psess - Session to update * pszArg - File name to add * perr - ERROR structure * * Exit-Success: * Returns HFILESPEC; psess updated. * * Exit-Failure: * Returns NULL; perr filled in with error. */ HFILESPEC addFileSpec(PSESSION psess, char *pszArg, PERROR perr) { HFILESPEC hfspec; AssertSess(psess); //** Make sure a list exists if (psess->hflist == NULL) { if (!(psess->hflist = FLCreateList(perr))) { return FALSE; } } //** Add file to list if (!(hfspec = FLAddFile(psess->hflist, pszArg, NULL, perr))) { return NULL; } //** Success return hfspec; } /* addFileSpec() */ #ifdef ASSERT /*** fnafReport - Report assertion failure * * NOTE: See asrt.h for entry/exit conditions. */ FNASSERTFAILURE(fnafReport) { printf("\n%s:(%d) Assertion Failed: %s\n",pszFile,iLine,pszMsg); exit(1); } #endif // ASSERT /*** printError - Display error on stdout * * Entry * perr - ERROR structure to print * * Exit-Success * Writes error message to stdout. */ void printError(PSESSION psess, PERROR perr) { //** Make sure error starts on a new line if (psess->fNoLineFeed) { printf("\n"); psess->fNoLineFeed = FALSE; } //** General error printf("%s: %s\n",pszDDERR_ERROR,perr->ach); } /*** pszFromMSDOSTime - Convert MS-DOS file date/time to string * * Entry: * psz - Buffer to receive formatted date/time * cb - Length of psz buffer * date - MS-DOS FAT file system date format (see below) * time - MS-DOS FAT file system time format (see below) * * Exit: * *ptm filled in * * NOTE: This is the interpretation of the MS-DOS date/time values: * * Time Bits cBits Meaning * --------- ----- ---------------------------------------- * 0 - 4 5 Number of two-second increments (0 - 29) * 5 - 10 6 Minutes (0 - 59) * 11 - 15 5 Hours (0 - 23) * * Date Bits cBits Meaning * --------- ----- ---------------------------------------- * 0 - 4 5 Day (1 - 31) * 5 - 8 4 Month (1 - 12) * 9 - 15 7 Year since 1980 (for example, 1994 is stored as 14) */ void pszFromMSDOSTime(char *psz, int cb, WORD date, WORD time) { int sec; int min; int hour; int day; int month; int year; char *pszAMPM; // AM/PM string sec = (time & 0x1f) << 1; min = (time >> 5) & 0x3f; hour = (time >> 11) & 0x1f; //** Get am/pm extension, and map 0 to 12 if (hour >= 12) { pszAMPM = pszDD_TIME_PM; hour -= 12; } else { pszAMPM = pszDD_TIME_AM; } if (hour == 0) { hour = 12; } day = (date & 0x1f); month = (date >> 5) & 0x0f; year = ((date >> 9) & 0x7f) + 1980; MsgSet(psz,pszDD_DATE_TIME, "%02d%02d%02d%2d%02d%02d%s", month, day, year, hour, min, sec, pszAMPM); } /* pszFromMSDOSTime() */