www.pudn.com > cab文件压缩、解压程序源代码.zip > Cabinet.cpp
//--------------------------------------------------------------------------- // Copyright (C) 1998, Interscope Ltd. All rights reserved. // Reproduction or distribution of this program, or any portion of it, // is permitted only if this header is kept as it is. // For more information, contact: // // Interscope Ltd., 5 Culturii St., 5th floor, 4800 Baia Mare, Romania // Phone/Fax: +40-62-215023 // E-mail: office@interscope.ro // // $Author: Levente Farkas $ // $Date: 5/12/98 11:49p $ // $Modtime: 4/27/98 6:50a $ // $Revision: 68 $ // $Archive: /Interscope/Thebe/InstallMaster/Cabinet.cpp $ // $Workfile: Cabinet.cpp $ //----------------------------------------------------------------------- #ifdef __STDAFX__ #include "StdAfx.H" #endif #include#include #include #include #include #ifndef __MFC__ #include "Trace.H" #endif #include "AssertX.H" #include "Reminder.Hpp" #include "Cabinet.Hpp" //--- Debugee -------------------------------------------------------------- #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #ifdef __MFC__ #define new DEBUG_NEW #endif // __MFC__ #endif // _DEBUG //------------------------------------------------------------------------- // Pre : // Post : // Globals : // I/O : // Task : Allocate memory //------------------------------------------------------------------------- void HUGE * FAR DIAMONDAPI CCabinetBase::MemAlloc(ULONG cbSize) { #ifdef __TRACE_CAB_MEMORY__ TRACEFN(_T("CCabinetBase::MemAlloc(%lu)"),cbSize); #endif return malloc(cbSize); } //------------------------------------------------------------------------- // Pre : // Post : // Globals : // I/O : // Task : Free allocated memory //------------------------------------------------------------------------- void FAR DIAMONDAPI CCabinetBase::MemFree(void HUGE *ptrMemory) { #ifdef __TRACE_CAB_MEMORY__ TRACEFN(_T("CCabinetBase::MemFree(0x%lx)\n"),ptrMemory); #endif free(ptrMemory); } //------------------------------------------------------------------------- // Pre : If not NULL, the wild parameter pv is a pointer 2 an instance // of a class CCabinetBase // Post : // Globals : // I/O : // Task : Open a file //------------------------------------------------------------------------- int FAR DIAMONDAPI CCabinetBase::FileOpen(char FAR *pszFile, int oFlag, int pMode, int FAR *ptrErr, void FAR *pv) { #if defined(__TRACE_CAB_COMPRESSION__) || defined(__TRACE_CAB_EXTRACTION__) TRACEFN(_T("CCabinetBase::FileOpen(%s,0x%04x,0x%04x)\n"),pszFile,oFlag,pMode); #endif // No error yet ASSERTX(ptrErr); *ptrErr =NOERROR; // Text-mode files are not supported, everything is // considered 2 be raw binary if((oFlag & _O_TEXT) == _O_TEXT) { ASSERTX(FALSE); *ptrErr =EINVAL; return -1; } // Extract access mode from oFlag DWORD dwDesiredAccess =GENERIC_READ; if((oFlag & _O_WRONLY) == _O_WRONLY) dwDesiredAccess =GENERIC_WRITE; else if((oFlag & _O_RDWR) == _O_RDWR) dwDesiredAccess |=GENERIC_WRITE; if(dwDesiredAccess == GENERIC_READ) // When we only want 2 read from a file, make sure // we open it with share-read, otherwise we won't be // able 2 read from files which are in use by Windows pMode |=_S_IREAD; // Extract creation mode from oFlag DWORD dwCreateMode; if((oFlag & _O_CREAT) == _O_CREAT) { // Creating a file if((oFlag & _O_EXCL) == _O_EXCL) dwCreateMode =CREATE_NEW; else if((oFlag & _O_TRUNC) == _O_TRUNC) dwCreateMode =CREATE_ALWAYS; else dwCreateMode =OPEN_ALWAYS; } else { // Opening a file if((oFlag & _O_TRUNC) == _O_TRUNC) dwCreateMode =TRUNCATE_EXISTING; else dwCreateMode =OPEN_EXISTING; } // Extract other flags from oFlag DWORD dwFlagsAttrs =FILE_ATTRIBUTE_NORMAL; if((oFlag & _O_RANDOM) == _O_RANDOM) dwFlagsAttrs |=FILE_FLAG_RANDOM_ACCESS; if((oFlag & _O_SEQUENTIAL) == _O_SEQUENTIAL) dwFlagsAttrs |=FILE_FLAG_SEQUENTIAL_SCAN; if((oFlag & _O_CREAT) == _O_CREAT) { // Thease are valid only when creating a new file if((oFlag & _O_SHORT_LIVED) == _O_SHORT_LIVED) dwFlagsAttrs |=FILE_ATTRIBUTE_TEMPORARY; if((oFlag & _O_TEMPORARY) == _O_TEMPORARY) dwFlagsAttrs |=FILE_FLAG_DELETE_ON_CLOSE; } // Converting share mode DWORD dwShareMode =0; if((pMode & _S_IREAD) == _S_IREAD) dwShareMode |=FILE_SHARE_READ; if((pMode & _S_IWRITE) == _S_IWRITE) dwShareMode |=FILE_SHARE_WRITE; HANDLE hFile =CreateFile(pszFile,dwDesiredAccess,dwShareMode,NULL,dwCreateMode,dwFlagsAttrs,NULL); BOOL bResult =(hFile != INVALID_HANDLE_VALUE); if(bResult) { if((oFlag & _O_APPEND) == _O_APPEND) // Move file pointer 2 the end of the file first SetFilePointer(hFile,0,NULL,FILE_END); } else { // Store the error code DWORD dwError =GetLastError(); switch(dwError) { case ERROR_ACCESS_DENIED: case ERROR_LOCK_VIOLATION: case ERROR_SHARING_VIOLATION: *ptrErr =EACCES; break; case ERROR_INVALID_PARAMETER: *ptrErr =EINVAL; break; case ERROR_FILE_EXISTS: case ERROR_ALREADY_EXISTS: *ptrErr =EEXIST; break; case ERROR_TOO_MANY_OPEN_FILES: *ptrErr =EMFILE; break; case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: *ptrErr =ENOENT; break; default: *ptrErr =(int)dwError; } } return bResult ? (int)hFile : -1; } //------------------------------------------------------------------------- // Pre : If not NULL, the wild parameter pv is a pointer 2 an instance // of a class CCabinetBase // Post : // Globals : // I/O : // Task : Read cbRead bytes from file specified by handle into buffer ptrBuffer //------------------------------------------------------------------------- UINT FAR DIAMONDAPI CCabinetBase::FileRead(int hFile, void FAR *ptrBuffer, UINT cbRead, int FAR *ptrErr, void FAR *pv) { #if defined(__TRACE_CAB_COMPRESSION__) || defined(__TRACE_CAB_EXTRACTION__) TRACEFN(_T("CCabinetBase::FileRead(%d,0x%p,%u)\n"),hFile,ptrBuffer,cbRead); #endif // No error yet ASSERTX(ptrErr); *ptrErr =NOERROR; DWORD dwBytesRead; BOOL bResult =ReadFile((HANDLE)hFile,ptrBuffer,cbRead,&dwBytesRead,NULL); // Check 4 end-of-file condition if(bResult && (dwBytesRead == 0)) // EOF return 0; if(!bResult) // Store the error code *ptrErr =EBADF; return bResult ? (UINT)dwBytesRead : (UINT)-1; } //------------------------------------------------------------------------- // Pre : If not NULL, the wild parameter pv is a pointer 2 an instance // of a class CCabinetBase // Post : // Globals : // I/O : // Task : Write cbWrite bytes from buffer ptrBuffer int file specified by handle //------------------------------------------------------------------------- UINT FAR DIAMONDAPI CCabinetBase::FileWrite(int hFile, void FAR *ptrBuffer, UINT cbWrite, int FAR *ptrErr, void FAR *pv) { #if defined(__TRACE_CAB_COMPRESSION__) || defined(__TRACE_CAB_EXTRACTION__) TRACEFN(_T("CCabinetBase::FileWrite(%d,0x%p,%u)\n"),hFile,ptrBuffer,cbWrite); #endif // No error yet ASSERTX(ptrErr); *ptrErr =NOERROR; DWORD dwBytesWritten; BOOL bResult =WriteFile((HANDLE)hFile,ptrBuffer,cbWrite,&dwBytesWritten,NULL); if(!bResult) { // Store the error code DWORD dwError =GetLastError(); switch(dwError) { case ERROR_HANDLE_DISK_FULL: case ERROR_DISK_FULL: *ptrErr =ENOSPC; break; default: *ptrErr =EBADF; } } return bResult ? (UINT)dwBytesWritten : (UINT)-1; } //------------------------------------------------------------------------- // Pre : If not NULL, the wild parameter pv is a pointer 2 an instance // of a class CCabinetBase // Post : // Globals : // I/O : // Task : Seek the file pointer //------------------------------------------------------------------------- long FAR DIAMONDAPI CCabinetBase::FileSeek(int hFile, long offset, int seekType, int FAR *ptrErr, void FAR *pv) { #if defined(__TRACE_CAB_COMPRESSION__) || defined(__TRACE_CAB_EXTRACTION__) TRACEFN(_T("CCabinetBase::FileSeek(%d,%ld,%d)\n"),hFile,offset,seekType); #endif // No error yet ASSERTX(ptrErr); *ptrErr =NOERROR; // Convert seek type DWORD dwMoveMethod; switch(seekType) { case SEEK_SET: dwMoveMethod =FILE_BEGIN; break; case SEEK_CUR: dwMoveMethod =FILE_CURRENT; break; case SEEK_END: dwMoveMethod =FILE_END; break; default: // This should never happen ASSERTX(FALSE); *ptrErr =EINVAL; return -1; } DWORD dwNewOffset =SetFilePointer((HANDLE)hFile,offset,NULL,dwMoveMethod); BOOL bResult =(dwNewOffset != (DWORD)-1); if(!bResult) { // Store the error code DWORD dwError =GetLastError(); switch(dwError) { case ERROR_INVALID_PARAMETER: *ptrErr =EINVAL; break; default: *ptrErr =EBADF; } } return bResult ? (long)dwNewOffset : -1; } //------------------------------------------------------------------------- // Pre : If not NULL, the wild parameter pv is a pointer 2 an instance // of a class CCabinetBase // Post : // Globals : // I/O : // Task : Close this file //------------------------------------------------------------------------- int FAR DIAMONDAPI CCabinetBase::FileClose(int hFile, int FAR *ptrErr, void FAR *pv) { #if defined(__TRACE_CAB_COMPRESSION__) || defined(__TRACE_CAB_EXTRACTION__) TRACEFN(_T("CCabinetBase::FileClose(%d)\n"),hFile); #endif // No error yet ASSERTX(ptrErr); *ptrErr =NOERROR; BOOL bResult =CloseHandle((HANDLE)hFile); if(!bResult) // Store the error code *ptrErr =EBADF; return bResult ? 0 : -1; } #ifdef __CAB_BUILD__ //------------------------------------------------------------------------- // Pre : // Post : // Globals : // I/O : // Task : Create a cabinet builder object //------------------------------------------------------------------------- CCabinetBuilder::CCabinetBuilder(USHORT nID, ULONG cbFirstCabSize, ULONG cbCabSize, ULONG cbTreshold): m_cbFirstCabSize(cbFirstCabSize), m_cbCabSize(cbCabSize), m_cbTreshold(cbTreshold), m_nID(nID), m_cbReserveCFHeader(0), m_cbReserveCFFolder(0), m_cbReserveCFData(0), m_hContext(NULL) { #ifdef __TRACE_CAB_COMPRESSION__ TRACEFN(_T("CCabinetBuilder::CCabinetBuilder\n")); #endif // Inited, but before we can do actual compression client must call InitCabinet } #endif #ifdef __CAB_BUILD__ //------------------------------------------------------------------------- // Pre : The wild parameter pv is a pointer 2 an instance of class CCabinetBuilder // Post : // Globals : // I/O : // Task : Compression interface wants 2 delete this file //------------------------------------------------------------------------- int FAR DIAMONDAPI CCabinetBuilder::FileDelete(char FAR *pszFile, int FAR *ptrErr, void FAR *pv) { #ifdef __TRACE_CAB_COMPRESSION__ TRACEFN(_T("CCabinetBuilder::FileDelete(%s)\n"),pszFile); #endif // No error yet ASSERTX(ptrErr); *ptrErr =NOERROR; BOOL bResult =DeleteFile(pszFile); if(!bResult) { // Store the error code DWORD dwError =GetLastError(); switch(dwError) { case ERROR_ACCESS_DENIED: case ERROR_LOCK_VIOLATION: case ERROR_SHARING_VIOLATION: *ptrErr =EACCES; break; case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: *ptrErr =ENOENT; break; default: *ptrErr =(int)dwError; } } return bResult; } #endif #ifdef __CAB_BUILD__ //------------------------------------------------------------------------- // Pre : pszTempName points 2 a buffer to receive complete tempfile name // cbTempName is size of pszTempName buffer // The wild parameter pv is a pointer 2 an instance of class CCabinetBuilder // Post : Return TRUE on success // Globals : // I/O : // Task : Compression interface requests a temporary filename //------------------------------------------------------------------------- BOOL FAR DIAMONDAPI CCabinetBuilder::GetTempFile(char *pszTempName, int cbTempName, void FAR *pv) { #ifdef __TRACE_CAB_COMPRESSION__ TRACEFN(_T("CCabinetBuilder::GetTempFile... ")); #endif // Manufacture temporary path CPath temp_path; temp_path.TempDirectory(); if(temp_path.CreateTempName(_T("int"))) { // Got a temp filename string strTempFileName((LPCTSTR)temp_path); if(cbTempName < strTempFileName.length()) // Buffer too small return FALSE; STRCPY(pszTempName,strTempFileName.c_str()); #ifdef __TRACE_CAB_COMPRESSION__ TRACEX(_T("%s\n"),pszTempName); #endif return TRUE; } #ifdef __TRACE_CAB_COMPRESSION__ TRACEX(_T("Failed\n")); #endif // Could not get a temp filename return FALSE; } #endif #ifdef __CAB_BUILD__ //------------------------------------------------------------------------- // Pre : The wild parameter pv is a pointer 2 an instance of class CCabinetBuilder // Post : // Globals : // I/O : // Task : Opens a file and returns its datestamp, timestamp, and attributes at // compression interface request //------------------------------------------------------------------------- int FAR DIAMONDAPI CCabinetBuilder::GetFileInfo(char *pszName, USHORT *pdate, USHORT *ptime, USHORT *pattribs, int FAR *ptrErr, void FAR *pv) { #ifdef __TRACE_CAB_COMPRESSION__ TRACEFN(_T("CCabinetBuilder::GetFileInfo\n")); #endif // No error yet ASSERTX(ptrErr); *ptrErr =NOERROR; BY_HANDLE_FILE_INFORMATION finfo; FILETIME filetime; HANDLE handle; DWORD attrs; // We need a Win32 type handle to get file date/time handle =CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if(handle == INVALID_HANDLE_VALUE) { // Could not open file goto LABEL_InfoError; } if(!GetFileInformationByHandle(handle,&finfo)) { // Could not retrieve file info CloseHandle(handle); goto LABEL_InfoError; } FileTimeToLocalFileTime(&finfo.ftLastWriteTime,&filetime); FileTimeToDosDateTime(&filetime,pdate,ptime); if((attrs =GetFileAttributes(pszName)) == 0xFFFFFFFF) { // Failure *pattribs =0; } else { // Mask out all other bits except these, since other // bits are used by the cabinet format 2 indicate a // special meaning *pattribs =(int)(attrs & (_A_RDONLY | _A_SYSTEM | _A_HIDDEN | _A_ARCH)); } CloseHandle(handle); // We've got all the info we needed, // now open the file so that the class' file utility // functions can work with it return FileOpen(pszName,_O_RDONLY | _O_BINARY,0,ptrErr,pv); LABEL_InfoError: // Store the error code DWORD dwError =GetLastError(); switch(dwError) { case ERROR_ACCESS_DENIED: case ERROR_LOCK_VIOLATION: case ERROR_SHARING_VIOLATION: *ptrErr =EACCES; break; case ERROR_INVALID_PARAMETER: *ptrErr =EINVAL; break; case ERROR_TOO_MANY_OPEN_FILES: *ptrErr =EMFILE; break; case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: *ptrErr =ENOENT; break; default: *ptrErr =(int)dwError; } return -1; } #endif #ifdef __CAB_BUILD__ //------------------------------------------------------------------------- // Pre : The wild parameter pv is a pointer 2 an instance of class CCabinetBuilder // Post : Return FALSE 2 abort cabinet creation // Globals : // I/O : // Task : The compression interface wishes to create a new cabinet, which // will happen whenever the size of the cabinet is about to exceed // the media size specified // The cabinet name MUST be modified. Also, the disk name, media // size, folder threshold, etc. parameters may also be modified //------------------------------------------------------------------------- BOOL FAR DIAMONDAPI CCabinetBuilder::GetNextCabinet(PCCAB pccab, ULONG cbPrevCab, void FAR *pv) { #ifdef __TRACE_CAB_COMPRESSION__ TRACEFN(_T("CCabinetBuilder::GetNextCabinet(%u)\n"),cbPrevCab); #endif if(pccab) { // Cabinet counters already incremented by FCI, updating the names CCabinetBuilder *pcb =(CCabinetBuilder *)pv; ASSERTX(pcb); pccab->iDisk++; pcb->ManufactureDiskName(pccab); pcb->ManufactureCabinetName(pccab); // Update the media size after the first cabinet pccab->cb =pcb->m_cbCabSize; } return TRUE; } #endif #ifdef __CAB_BUILD__ //--------------------------------------------------------------------------- // Pre : pszFile is name of file // cbFile is // Post : Return FALSE 2 abort // Globals : // I/O : // Task : Simple notification that a file was committed 2 a cabinet //--------------------------------------------------------------------------- BOOL CCabinetBuilder::NotifyPlaced(char *pszFile, long cbFile, BOOL bContinuation, int nDisk, char *pszCabinet) { #ifdef __TRACE_CAB_COMPRESSION__ TRACEFN(_T("CCabinetBuilder::NotifyPlaced(%s,%ld,%u,%d,%s)\n"),pszFile,cbFile,bContinuation,nDisk,pszCabinet); #endif return TRUE; } #endif #ifdef __CAB_BUILD__ //------------------------------------------------------------------------- // Pre : The wild parameter pv is a pointer 2 an instance of class CCabinetBuilder // Post : Return -1 to abort // Globals : // I/O : // Task : Notification from compression interface that a file was // committed 2 a cabinet //------------------------------------------------------------------------- int FAR DIAMONDAPI CCabinetBuilder::NotifyFilePlaced(PCCAB pccab, char *pszFile, long cbFile, BOOL bContinuation, void FAR *pv) { #ifdef __TRACE_CAB_COMPRESSION__ TRACEFN(_T("CCabinetBuilder::NotifyFilePlaced(%s,%ld,%u,%d,%d)\n"),pszFile,cbFile,bContinuation,pccab->iDisk,pccab->iCab); #endif // Call the client's notification proc too ASSERTX(pccab != NULL); CCabinetBuilder *pcb =(CCabinetBuilder *)pv; ASSERTX(pcb != NULL); BOOL ok_2_continue =pcb->NotifyPlaced(pszFile,cbFile,bContinuation,pccab->iDisk,pccab->szCab); return ok_2_continue ? 0 : -1; } #endif #ifdef __CAB_BUILD__ //--------------------------------------------------------------------------- // Pre : cbOriginal is the size of uncompressed block // cbCompressed is the size of the block after compression // Post : Return FALSE 2 abort // Globals : // I/O : // Task : Simple notification that FCI is compressing a block in2 a folder // Useful 2 keep track of compression statistics //--------------------------------------------------------------------------- BOOL CCabinetBuilder::NotifyCompress(ULONG cbOriginal, ULONG cbCompressed) { #ifdef __TRACE_CAB_COMPRESSION__ TRACEFN(_T("CCabinetBuilder::NotifyCompress(%lu,%lu)\n"),cbOriginal,cbCompressed); #endif return TRUE; } #endif #ifdef __CAB_BUILD__ //--------------------------------------------------------------------------- // Pre : cbSize is the total size of folder // cbWritten is the amount of folder copied to cabinet so far // Post : Return FALSE 2 abort // Globals : // I/O : // Task : Simple notification that FCI is adding a folder 2 a cabinet //--------------------------------------------------------------------------- BOOL CCabinetBuilder::NotifyFolderFlush(ULONG cbSize, ULONG cbWritten) { #ifdef __TRACE_CAB_COMPRESSION__ TRACEFN(_T("CCabinetBuilder::NotifyFolderFlush(%lu,%lu)\n"),cbSize,cbWritten); #endif return TRUE; } #endif #ifdef __CAB_BUILD__ //--------------------------------------------------------------------------- // Pre : cbEstimatedSize is the estimated cabinet size // cbActualSize is the actual cabinet size // Post : Return the desired cabinet size // Globals : // I/O : // Task : Simple notification that FCI is writing out a cabinet //--------------------------------------------------------------------------- ULONG CCabinetBuilder::NotifyCabinetFlush(ULONG cbEstimatedSize, ULONG cbActualSize) { #ifdef __TRACE_CAB_COMPRESSION__ TRACEFN(_T("CCabinetBuilder::NotifyCabinetFlush(%lu,%lu)\n"),cbEstimatedSize,cbActualSize); #endif return cbActualSize; } #endif #ifdef __CAB_BUILD__ //------------------------------------------------------------------------- // Pre : The wild parameter pv is a pointer 2 an instance of class CCabinetBuilder // If typeStatus == statusFile CFI is compressing a block into a folder // cb1 = Size of compressed block // cb2 = Size of uncompressed block // // If typeStatus == statusFolder CFI is adding a folder to a cabinet // cb1 = Amount of folder copied to cabinet so far // cb2 = Total size of folder // // If typeStatus == statusCabinet CFI is writing out a complete cabinet // cb1 = Estimated cabinet size that was previously passed to GetNextCabinet() // cb2 = Actual cabinet size // Post : Return -1 to signal that FCI should abort except if typeStatus == statusCabinet, when // the return value is the desired client size for cabinet file. FCI updates the maximum // cabinet size remaining using this value. This allows a client to generate multiple // cabinets per disk, and have FCI limit the size correctly (the client can do cluster // size rounding on the cabinet size!) // The client should either return cb2, or round cb2 up to some larger value and return that // Globals : // I/O : // Task : Notification from compression interface that some action is in progress //------------------------------------------------------------------------- long FAR DIAMONDAPI CCabinetBuilder::NotifyProgress(UINT typeStatus, ULONG cb1, ULONG cb2, void FAR *pv) { #ifdef __TRACE_CAB_COMPRESSION__ TRACEFN(_T("CCabinetBuilder::NotifyProgress(%u,%lu,%lu)\n"),typeStatus,cb1,cb2); #endif // Handle notification long result =0; BOOL ok_2_continue; CCabinetBuilder *pcb =(CCabinetBuilder *)pv; ASSERTX(pcb); switch(typeStatus) { case statusFile: // A block is being compressed ok_2_continue =pcb->NotifyCompress(cb2,cb1); result =ok_2_continue ? 0 : -1; break; case statusFolder: // Flushing a folder ok_2_continue =pcb->NotifyFolderFlush(cb2,cb1); result =ok_2_continue ? 0 : -1; break; case statusCabinet: // Writing out a cabinet result =pcb->NotifyCabinetFlush(cb1,cb2); break; } return result; } #endif #ifdef __CAB_BUILD__ //------------------------------------------------------------------------- // Pre : // Post : // Globals : // I/O : // Task : Build a disk name and store it in the passed CCAB struct //------------------------------------------------------------------------- void CCabinetBuilder::ManufactureDiskName(PCCAB cabinfo) { if(cabinfo) { // Manufacture name TCHAR disk_name[CB_MAX_DISK_NAME]; SPRINTF(disk_name,_T("%s%d"),m_strDiskName.c_str(),cabinfo->iDisk); STRNCPY(cabinfo->szDisk,disk_name,CB_MAX_DISK_NAME); } } #endif #ifdef __CAB_BUILD__ //------------------------------------------------------------------------- // Pre : // Post : // Globals : // I/O : // Task : Build a cabinet name and store it in the passed CCAB struct //------------------------------------------------------------------------- void CCabinetBuilder::ManufactureCabinetName(PCCAB cabinfo) { if(cabinfo) { // Manufacture name TCHAR cabinet_name[CB_MAX_CABINET_NAME]; SPRINTF(cabinet_name,_T("%s%d.cab"),m_strCabinetName.c_str(),cabinfo->iCab); STRNCPY(cabinfo->szCab,cabinet_name,CB_MAX_CABINET_NAME); } } #endif #ifdef __CAB_BUILD__ //------------------------------------------------------------------------- // Pre : If bCreatePath is TRUE, the .CAB file's path is created if not exists // Post : Return TRUE on success // Globals : // I/O : // Task : Initialize the cabinet //------------------------------------------------------------------------- BOOL CCabinetBuilder::InitCabinet(LPCTSTR lpcszCabPath, LPCTSTR lpcszCabName, LPCTSTR lpcszDiskName, BOOL bCreatePath) { // Init the name of the first cabinet and the number of the first disk TCHAR cabinet_name[CB_MAX_CABINET_NAME]; SPRINTF(cabinet_name,_T("%s%d.cab"),lpcszCabName,1); m_nLastStartDisk =1; m_strLastCabinet =cabinet_name; // Check path (and create it if not exists and so specified) string cab_str(lpcszCabPath); EnsureTrailingBackslash(cab_str); m_CabinetPath =cab_str.c_str(); if(!m_CabinetPath.DirectoryExists()) { // Cabinet path does not exist if(!bCreatePath) return FALSE; m_CabinetPath.CreateDirectory(); } // Prepare compression parameters m_strCabinetName =lpcszCabName; m_strDiskName =lpcszDiskName; m_strDiskName +=_T(" "); // So that the name of the disks will be e.g. "Disk 1" and we can use // this name to prompt user for new disks when necessary ZeroMemory(&m_CabinetInfo,sizeof(m_CabinetInfo)); m_CabinetInfo.setID =m_nID; m_CabinetInfo.cb =m_cbFirstCabSize; m_CabinetInfo.cbFolderThresh =m_cbTreshold; m_CabinetInfo.cbReserveCFHeader =m_cbReserveCFHeader; m_CabinetInfo.cbReserveCFFolder =m_cbReserveCFFolder; m_CabinetInfo.cbReserveCFData =m_cbReserveCFData; m_CabinetInfo.iCab =1; m_CabinetInfo.iDisk =1; STRNCPY(m_CabinetInfo.szCabPath,cab_str.c_str(),CB_MAX_CAB_PATH); ManufactureDiskName(&m_CabinetInfo); ManufactureCabinetName(&m_CabinetInfo); // Create a compression context m_hContext =FCICreate(&m_CabinetError, NotifyFilePlacedProc(), MemAllocProc(), MemFreeProc(), FileOpenProc(), FileReadProc(), FileWriteProc(), FileCloseProc(), FileSeekProc(), FileDeleteProc(), GetTempFileProc(), &m_CabinetInfo, this); // Pass an instance 2 ourselves, so that we are accessible from the statix if(m_hContext == NULL) // Could not create compression context // Client can call GetErrorCode() 2 get a code of what happened return FALSE; return (m_bStatus =TRUE); // Success } #endif #ifdef __CAB_BUILD__ //------------------------------------------------------------------------- // Pre : lpcszFileName is the fully qualified name of file 2 add // lpcszStoreAs is the name without path info under which 2 store the file in the cabinet // Post : Return TRUE on success // Globals : // I/O : // Task : Add a file in2 the cabinet //------------------------------------------------------------------------- BOOL CCabinetBuilder::AddFile(LPTSTR lpszFileName, LPTSTR lpszStoreAs, BOOL bCompress, BOOL bExecuteOnExtract) { // Check our internal status CPath file_path(lpszFileName); if(!m_bStatus || !file_path.Exists()) return FALSE; // Strip path info from filename 2 add, in case we are not specified // any name 2 store file being added as string strStrippedName; TCHAR szStrippedName[MAX_PATH]; file_path.GetNameExtension(strStrippedName); STRNCPY(szStrippedName,strStrippedName.c_str(),sizeof(szStrippedName)/sizeof(TCHAR)); // Adding file... BOOL added_OK =FCIAddFile(m_hContext, lpszFileName, lpszStoreAs ? lpszStoreAs : szStrippedName, bExecuteOnExtract, GetNextCabinetProc(), NotifyProgressProc(), GetFileInfoProc(), bCompress ? tcompTYPE_MSZIP : tcompTYPE_NONE); if(!added_OK) { // Could not add the file 2 cabinet, cleanup FCIDestroy(m_hContext); m_hContext =NULL; m_bStatus =FALSE; } return added_OK; } #endif #ifdef __CAB_BUILD__ //------------------------------------------------------------------------- // Pre : // Post : Return TRUE on success // Globals : // I/O : // Task : Force the current feolder under construction 2 be completed // immediately and written 2 disk //------------------------------------------------------------------------- BOOL CCabinetBuilder::FlushFolder() { // Check our internal status if(!m_bStatus) return FALSE; return FCIFlushFolder(m_hContext,GetNextCabinetProc(),NotifyProgressProc()); } #endif #ifdef __CAB_BUILD__ //------------------------------------------------------------------------- // Pre : // Post : Return TRUE on success // Globals : // I/O : // Task : Force the current cabinet under construction 2 be completed // immediately and written 2 disk //------------------------------------------------------------------------- BOOL CCabinetBuilder::FlushCabinet() { // Check our internal status if(!m_bStatus) return FALSE; return FCIFlushCabinet(m_hContext,FALSE,GetNextCabinetProc(),NotifyProgressProc()); } #endif #ifdef __CAB_BUILD__ //------------------------------------------------------------------------- // Pre : // Post : // Globals : // I/O : // Task : Cleanup and delete a cabinet builder object //------------------------------------------------------------------------- CCabinetBuilder::~CCabinetBuilder() { #ifdef __TRACE_CAB_COMPRESSION__ TRACEFN(_T("CCabinetBuilder::~CCabinetBuilder\n")); #endif // Free the compression context (if any) if(m_bStatus && (m_hContext != NULL)) { FCIDestroy(m_hContext); } } #endif #ifdef __CAB_EXTRACT__ //------------------------------------------------------------------------- // Pre : // Post : // Globals : // I/O : // Task : Create a cabinet extractor object //------------------------------------------------------------------------- CCabinetExtractor::CCabinetExtractor() { // Allocate a decompression context m_hContext =FDICreate(MemAllocProc(), MemFreeProc(), FileOpenProc(), FileReadProc(), FileWriteProc(), FileCloseProc(), FileSeekProc(), cpu80386, &m_CabinetError); // Check if context allocated OK and update our internal status m_bStatus =(m_hContext != NULL); // Set the default extraction path in2 a temp folder m_DefaultExtractPath.TempDirectory(); } #endif #ifdef __CAB_EXTRACT__ //------------------------------------------------------------------------- // Pre : // Post : // Globals : // I/O : // Task : Open a file 4 decompression interface //------------------------------------------------------------------------- int FAR DIAMONDAPI CCabinetExtractor::FileOpen(char FAR *pszFile, int oFlag, int pMode) { return CCabinetBase::FileOpen(pszFile,oFlag,pMode,&errno,NULL); } #endif #ifdef __CAB_EXTRACT__ //------------------------------------------------------------------------- // Pre : // Post : // Globals : // I/O : // Task : Read cbRead bytes from file specified by handle into buffer ptrBuffer at // decompression interface request //------------------------------------------------------------------------- UINT FAR DIAMONDAPI CCabinetExtractor::FileRead(int hFile, void FAR *ptrBuffer, UINT cbRead) { return CCabinetBase::FileRead(hFile,ptrBuffer,cbRead,&errno,NULL); } #endif #ifdef __CAB_EXTRACT__ //------------------------------------------------------------------------- // Pre : // Post : // Globals : // I/O : // Task : Write cbWrite bytes from buffer ptrBuffer int file specified by handle at // decompression interface request //------------------------------------------------------------------------- UINT FAR DIAMONDAPI CCabinetExtractor::FileWrite(int hFile, void FAR *ptrBuffer, UINT cbWrite) { return CCabinetBase::FileWrite(hFile,ptrBuffer,cbWrite,&errno,NULL); } #endif #ifdef __CAB_EXTRACT__ //------------------------------------------------------------------------- // Pre : // Post : // Globals : // I/O : // Task : Compression interface wants 2 seek the file pointer //------------------------------------------------------------------------- long FAR DIAMONDAPI CCabinetExtractor::FileSeek(int hFile, long offset, int seekType) { return CCabinetBase::FileSeek(hFile,offset,seekType,&errno,NULL); } #endif #ifdef __CAB_EXTRACT__ //------------------------------------------------------------------------- // Pre : // Post : // Globals : // I/O : // Task : Decompression interface wants 2 close this file //------------------------------------------------------------------------- int FAR DIAMONDAPI CCabinetExtractor::FileClose(int hFile) { return CCabinetBase::FileClose(hFile,&errno,NULL); } #endif #ifdef __CAB_EXTRACT__ //------------------------------------------------------------------------- // Pre : If lpInfo is NULL, then no information is returned on the cabinet // only checking 2 see if it's a valid cabinet file // Post : Return TRUE if info extracted OK, FALSE if not a cabinet file or // there was some error // Globals : // I/O : // Task : Return info about a cabinet file //------------------------------------------------------------------------- BOOL CCabinetExtractor::GetCabinetInfo(LPCTSTR lpcszCabFile, CABINETINFO *lpInfo) { // Check our internal status if(!m_bStatus) return FALSE; // Open file because the decompression interface // wants an open file 2 get info about TCHAR szCabFile[MAX_PATH]; STRCPY(szCabFile,lpcszCabFile); int hFile =FileOpen(szCabFile,_O_RDONLY | _O_SEQUENTIAL,0); if(hFile == -1) // Could not open file return FALSE; // Get info... FDICABINETINFO info; BOOL is_cabinet =FDIIsCabinet(m_hContext,hFile,&info); if(is_cabinet && lpInfo) { // Store info about the cabinet lpInfo->m_cbSize =info.cbCabinet; lpInfo->m_nFolders =info.cFolders; lpInfo->m_nFiles =info.cFiles; lpInfo->m_nID =info.setID; lpInfo->m_nIndex =info.iCabinet; lpInfo->m_bHasReservedSpace =info.fReserve; lpInfo->m_bChainedPrev =info.hasprev; lpInfo->m_bChainedNext =info.hasnext; } // Close the file we've got info from FileClose(hFile); return is_cabinet; } #endif #ifdef __CAB_EXTRACT__ //------------------------------------------------------------------------- // Pre : // Post : // Globals : // I/O : // Task : Store a default extraction folder //------------------------------------------------------------------------- void CCabinetExtractor::SetDefaultExtractPath(LPCTSTR lpcszDefExtractPath) { if(lpcszDefExtractPath) { string str(lpcszDefExtractPath); EnsureTrailingBackslash(str); m_DefaultExtractPath =str.c_str(); } } #endif #ifdef __CAB_EXTRACT__ //------------------------------------------------------------------------- // Pre : lpcszCabFile is the full path 2 the first .cab file in set // bContinuous specifies whether 2 attempt processing possible following // cabinets in set (if any), or only extract files from the specified .cab file // Post : Return TRUE if file(s) extracted OK, FALSE if not a cabinet file or // there was some error // Globals : // I/O : // Task : Extract one or more files from a cabinet. Clients must implement // notification members, so that they can supply the extractor object // with info on each file 2 extract (e.g. if extract, where 2 extract etc.) //------------------------------------------------------------------------- BOOL CCabinetExtractor::ExtractFiles(LPCTSTR lpcszCabFile, BOOL bContinuous) { // Check our internal status if(!m_bStatus) return FALSE; // Make sure we've got a valid cabinet file CABINETINFO cabinfo; if(!GetCabinetInfo(lpcszCabFile,&cabinfo)) return FALSE; // Store the path and name of cabinet being extracted m_bNextCabinetReady =TRUE; m_CabinetPath =lpcszCabFile; m_CabinetPath.GetNameExtension(m_strCabinetName); m_CabinetPath.SetNameExtension(_T("")); BOOL bResult =TRUE; while(bResult) { // Start extraction... // This will extract all files in this .cab file, including the // ones which are continued in successive cabinets TCHAR sz_cabinet_path[MAX_PATH]; TCHAR sz_cabinet_name[MAX_PATH]; STRCPY(sz_cabinet_path,(LPCTSTR)m_CabinetPath); STRCPY(sz_cabinet_name,m_strCabinetName.c_str()); // Make sure the required cabinet is available // Only do this 4 second or subsequent passes string strNextCabinetPath((LPCTSTR)m_CabinetPath); m_CabinetError.erfOper =FDIERROR_NONE; while(!m_bNextCabinetReady) { // Got a cabinet, see if cabinet is already available at the expected location // and if not, ask our client 2 provide us with the path 2 the next cabinet CPath CabinetValidator; CabinetValidator.SetDriveDirectory(strNextCabinetPath.c_str()); CabinetValidator.SetNameExtension(m_strCabinetName.c_str()); if(CabinetValidator.Exists()) { // Check if file is a real cabinet if(GetCabinetInfo(CabinetValidator,&cabinfo)) { // Check if the cabinet we got is the one we want if(cabinfo.m_nIndex == m_nContinueIndex) { #pragma message(Reminder(_T("Also check set ID"))) // Yeah, we have the cabinet we need m_bNextCabinetReady =TRUE; } else { // Cabinet is the wrong one, again m_CabinetError.erfOper =FDIERROR_WRONG_CABINET; } } else { // File is not a cabinet, again m_CabinetError.erfOper =FDIERROR_NOT_A_CABINET; } } else { // No such file, again m_CabinetError.erfOper =FDIERROR_CABINET_NOT_FOUND; } if(m_bNextCabinetReady) { // Yeah, if we got here, then we have the cabinet we need m_CabinetError.erfOper =FDIERROR_NONE; m_CabinetPath.SetDriveDirectory(strNextCabinetPath.c_str()); STRCPY(sz_cabinet_path,(LPCTSTR)m_CabinetPath); } else { // We'll call NotifyNextCabinet until we get it right // or client aborts if(!NotifyNextCabinet(m_strCabinetName.c_str(),m_strContinueDisk.c_str(),strNextCabinetPath,m_CabinetError.erfOper)) { // Aborted m_CabinetError.erfOper =FDIERROR_USER_ABORT; m_CabinetError.fError =TRUE; return FALSE; } } } // Make sure that cabinet is available at the expected location // find out the index of the cabinet which is first 2 be extracted from m_strNextDisk.erase(); m_NextCabinet =m_CabinetPath; m_NextCabinet.SetNameExtension(m_strCabinetName.c_str()); ASSERTX(m_NextCabinet.Exists()); ASSERTX(GetCabinetInfo(m_NextCabinet,&cabinfo)); m_nNextIndex =cabinfo.m_nIndex; m_nContinueIndex =-1; // Some internal initializations m_nLastFileStartCabinetIndex =-1; m_nLastFileEndCabinetIndex =-1; m_bLastFileSkipped =FALSE; m_bCabinetSetFinished =FALSE; m_bNextCabinetReady =FALSE; // Extract files in this cabinet and any file that starts // in this cabinet but spans across cabinet boundaries // This might result in more than one cabinet file 2 be processed bResult =FDICopy(m_hContext, sz_cabinet_name, sz_cabinet_path, 0, NotifyProgressProc(), NULL, this); if(!bContinuous) // Extraction ends after one cabinet (and all cabinets // linked 2 this one by files spanning across cabinet boundaries) break; if(bResult) { // Check if we already processed all cabinets in this set // Also check that there was no file extracted from the last cabinet // Otherwise, we might get here when we just finished extracting the last // chunk of a file spanning across cabinet boundaries, having its // last chunk in the last cabinet, but the last cabinet also contains other files // and these won't get extracted if we stop now if(m_bCabinetSetFinished && (m_nLastFileStartCabinetIndex == m_nLastFileEndCabinetIndex)) // Yeah, we processed them all break; // Advancing 2 the next one // Here we should figure out which is the cabinet we should continue with if(m_bLastFileSkipped) { // The last file in this cabinet was skipped // We should continue with the next cabinet ASSERTX(!m_bCabinetSetFinished); m_ContinueCabinet =m_NextCabinet; m_strContinueDisk =m_strNextDisk; m_nContinueIndex =m_nNextIndex; } // Check if any file extracted from this cabinet if((m_nLastFileStartCabinetIndex == -1) && (m_nLastFileEndCabinetIndex == -1)) { // No files extracted, probably because this cabinet only // contains a chunk of a file spanning multiple cabinets // which is being skipped m_ContinueCabinet =m_NextCabinet; m_strContinueDisk =m_strNextDisk; m_nContinueIndex =m_nNextIndex; } m_CabinetPath =m_ContinueCabinet; m_CabinetPath.GetNameExtension(m_strCabinetName); m_CabinetPath.SetNameExtension(_T("")); m_bNextCabinetReady =FALSE; } } return bResult; } #endif #ifdef __CAB_EXTRACT__ //--------------------------------------------------------------------------- // Pre : nID is the ID of cabinet set // lpcszCabPath is cabinet path // lpcszCabName is next cabinet's name (might be empty) // lpcszDiskName is next disk's name (might be empty) // nCabIndex is 0-based index of cabinet being processed // Post : Return FALSE 2 abort // Globals : // I/O : // Task : Simple notification that the decompression interface wants info // about the next cabinet // If overridden by clients, they should also call this //--------------------------------------------------------------------------- BOOL CCabinetExtractor::NotifyCabinetInfo(USHORT nID, LPCTSTR lpcszCabPath, LPCTSTR lpcszCabName, LPCTSTR lpcszDiskName, USHORT nCabIndex) { #ifdef __TRACE_CAB_EXTRACTION__ TRACEFN(_T("CCabinetExtractor::NotifyCabinetInfo(%u,%s,%s,%s,%u)\n"),nID,lpcszCabPath,lpcszCabName,lpcszDiskName,nCabIndex); #endif // Some additional check, this should be an empty // string if this is the last cabinet in the set if(!STRLEN(lpcszDiskName)) m_bCabinetSetFinished =TRUE; m_ContinueCabinet =m_NextCabinet; m_strContinueDisk =m_strNextDisk; m_nContinueIndex =m_nNextIndex; if(!m_bCabinetSetFinished) { m_nNextIndex++; m_NextCabinet.SetDriveDirectory(lpcszCabPath); m_NextCabinet.SetNameExtension(lpcszCabName); m_strNextDisk.erase(); m_strNextDisk =lpcszDiskName; } return TRUE; } #endif #ifdef __CAB_EXTRACT__ //--------------------------------------------------------------------------- // Pre : lpcszFileName is name of file continued from a previous cabinet // lpcszFirstCab is name of cabinet where the file starts // lpcszFirstDisk is name of disk where the file starts // Post : Return FALSE 2 abort // Globals : // I/O : // Task : Simple notification that first file in the cabinet is continuation // from some previous cabinet //--------------------------------------------------------------------------- BOOL CCabinetExtractor::NotifyPartialFile(LPCTSTR lpcszFileName, LPCTSTR lpcszFirstCab, LPCTSTR lpcszFirstDisk) { #ifdef __TRACE_CAB_EXTRACTION__ TRACEFN(_T("CCabinetExtractor::NotifyPartialFile(%s,%s,%s)\n"),lpcszFileName,lpcszFirstCab,lpcszFirstDisk); #endif return TRUE; } #endif #ifdef __CAB_EXTRACT__ //--------------------------------------------------------------------------- // Pre : lpcszFileName is name of file being extracted // cbSize is real (uncompressed) size of file // Post : Return FALSE 2 abort // Globals : // I/O : // Task : Simple notification that a file is about 2 be extracted from cabinet // we can specify where 2 extract it by filling strExtractTo with an // extract path. Before returning, make sure that the path you specified exists // If you want 2 skip this file, and do not extract it, set bSkipFile 2 TRUE //--------------------------------------------------------------------------- BOOL CCabinetExtractor::NotifyFileCopy(LPCTSTR lpcszFileName, ULONG cbSize, string &strExtractTo, BOOL &bSkipFile) { #ifdef __TRACE_CAB_EXTRACTION__ TRACEFN(_T("CCabinetExtractor::NotifyFileCopy(%s,%lu,%s,%s)\n"),lpcszFileName,cbSize,strExtractTo.c_str(),bSkipFile ? _T("TRUE") : _T("FALSE")); #endif // Extracting files 2 default extraction path and // using the same name as stored in the cabinet file m_DefaultExtractPath.GetDriveDirectory(strExtractTo); EnsureTrailingBackslash(strExtractTo); strExtractTo +=lpcszFileName; return TRUE; } #endif #ifdef __CAB_EXTRACT__ //--------------------------------------------------------------------------- // Pre : lpcszFileName is the name of the extracted file // Post : Return FALSE 2 abort // Globals : // I/O : // Task : Simple notification that a file was extracted OK, now it's time 2 // set it's attributes and date/time. Clients can specify the date/time // and attributes they want the extracted file 2 have // Note that at the time when this notification is called, the file is // still open. If you want 2 manipulate the file after it's extracted, // you can do so from NotifyFileCopiedAndClosed() //--------------------------------------------------------------------------- BOOL CCabinetExtractor::NotifyFileCopied(LPCTSTR lpcszFileName, USHORT &nDate, USHORT &nTime, USHORT &nAttribs) { #ifdef __TRACE_CAB_EXTRACTION__ TRACEFN(_T("CCabinetExtractor::NotifyFileCopied(%s,%u,%u,%u)\n"),lpcszFileName,nDate,nTime,nAttribs); #endif return TRUE; } #endif #ifdef __CAB_EXTRACT__ //--------------------------------------------------------------------------- // Pre : lpcszFileName is the name of the extracted file // Post : Return FALSE 2 abort // Globals : // I/O : // Task : Simple notification that a file was extracted OK // At this time the decompression interface got it's hands off the // extracted file, clients can do whatever they want with it // Here clients can also use the members: // m_nLastFileStartCabinetIndex // m_nLastFileEndCabinetIndex //--------------------------------------------------------------------------- BOOL CCabinetExtractor::NotifyFileCopiedAndClosed(LPCTSTR lpcszFileName) { #ifdef __TRACE_CAB_EXTRACTION__ TRACEFN(_T("CCabinetExtractor::NotifyFileCopiedAndClosed(%s)\n"),lpcszFileName); #endif return TRUE; } #endif #ifdef __CAB_EXTRACT__ //--------------------------------------------------------------------------- // Pre : // Post : Return FALSE 2 abort // Globals : // I/O : // Task : Simple notification of file enumeration //--------------------------------------------------------------------------- BOOL CCabinetExtractor::NotifyEnumerate(USHORT nID, long &nCurrentPosition, USHORT &nFilesRemaining) { #ifdef __TRACE_CAB_EXTRACTION__ TRACEFN(_T("CCabinetExtractor::NotifyEnumerate(%u,%ld,%u)\n"),nID,nCurrentPosition,nFilesRemaining); #endif return TRUE; } #endif #ifdef __CAB_EXTRACT__ //--------------------------------------------------------------------------- // Pre : lpcszNextCab is the name of the next cabinet // lpcszNextDisk is the name of the next disk // nErrorCode is the last error code (The decompression interface will keep calling this, // until the correct cabinet is returned or the extraction is aborted) // Post : Return FALSE 2 abort // Globals : // I/O : // Task : Simple notification that another cabinet is needed // Clients should here locate the next cabinet (eventually asking // users 4 the next disk or allowing browse 4 the next cabinet's location) // and return it 2 the decompression engine // Before returning a path, make sure that there is a cabinet with the proper name // (eventually also checking that is from the correct set) and is readable //--------------------------------------------------------------------------- BOOL CCabinetExtractor::NotifyNextCabinet(LPCTSTR lpcszNextCab, LPCTSTR lpcszNextDisk, string &strNextCabPath, int nErrorCode) { #ifdef __TRACE_CAB_EXTRACTION__ TRACEFN(_T("CCabinetExtractor::NotifyNextCabinet(%s,%s,%s,%d)\n"),lpcszNextCab,lpcszNextDisk,strNextCabPath.c_str(),nErrorCode); #endif // Assuming that next cabinet file is in the same folder return TRUE; } #endif #ifdef __CAB_EXTRACT__ //------------------------------------------------------------------------- // Pre : The wild pointer notifyInfo->pv field points 2 a CCabinetExtractor // If notifyType == fdintCABINET_INFO: // Called exactly once for each cabinet opened by FDICopy(), including // continuation cabinets opened due to file(s) spanning cabinet // boundaries. Primarily intended to permit 2 automatically select // the next cabinet in a cabinet sequence even if not copying files // that span cabinet boundaries // notifyInfo->psz1 is name of next cabinet (no path) // notifyInfo->psz2 is name of next disk // notifyInfo->psz3 is cabinet path name // notifyInfo->setID is cabinet set ID // notifyInfo->iCabinet is Cabinet number within cabinet set of the cabinet being processed now (0-based) // This call is made *every* time a new cabinet is examined by // FDICopy(). So if "foo2.cab" is examined because a file is // continued from "foo1.cab", and then you call FDICopy() again // on "foo2.cab", you will get *two* fdintCABINET_INFO calls // // If notifyType == fdintCOPY_FILE: // Called for each file that *starts* in the current cabinet, giving // the client the opportunity to request that the file be copied or skipped // notifyInfo->psz1 is file name in cabinet // notifyInfo->cb is uncompressed size of file // notifyInfo->date is file date // notifyInfo->time is file time // notifyInfo->attribs is file attributes // notifyInfo->iFolder is file's folder index // // If notifyType == fdintCLOSE_FILE_INFO: // Called after all of the data has been written to a target file. // This function must close the file and set the file date, time and attributes // notifyInfo->psz1 is file name in cabinet // notifyInfo->hf is file handle // notifyInfo->date is file date // notifyInfo->time is file time // notifyInfo->attribs is file attributes // notifyInfo->iFolder is file's folder index // notifyInfo->cb is Run After Extract flag (0 don't run, 1 run) // FDI assumes that the target file was closed, even if this // callback returns failure. FDI will NOT attempt to use // the PFNCLOSE function supplied on FDICreate() to close the file // // If notifyType == fdintPARTIAL_FILE: // Called for files at the front of the cabinet that are CONTINUED // from a previous cabinet. This callback occurs only when FDICopy is // started on second or subsequent cabinet in a series that has files // continued from a previous cabinet // notifyInfo->psz1 is file name of file CONTINUED from a PREVIOUS cabinet // notifyInfo->psz2 is name of cabinet where file starts // notifyInfo->psz3 is name of disk where file starts // // If notifyType == fdintENUMERATE: // Called once after a call to FDICopy() starts scanning a CAB's // CFFILE entries, and again when there are no more CFFILE entries. // If CAB spanning occurs, an additional call will occur after the // first spanned file is completed. If the notifyInfo->iFolder value is // changed from zero, additional calls will occur next time it reaches // zero. If iFolder is changed to zero, FDICopy will terminate, as if // there were no more CFFILE entries. Primarily intended to allow an // application with it's own file list to help FDI advance quickly to // a CFFILE entry of interest. Can also be used to allow an // application to determine the cb values for each file in the CAB // notifyInfo->cb is current CFFILE position // notifyInfo->iFolder is number of files remaining // notifyInfo->setID is current CAB's setID value // This call can be ignored by applications which want normal file // searching. The application can adjust the supplied values to // force FDICopy() to continue it's search at another location, or // to force FDICopy() to terminate the search, by setting iFolder to 0. // (FDICopy() will report no error when terminated this way) // FDI has no means to verify the supplied cb or iFolder values // Arbitrary values are likely to cause undesirable results. An // application should cross-check notifyInfo->setID to be certain the // external database is in sync with the CAB. Reverse-skips are OK // (but may be inefficient) unless fdintNEXT_CABINET has been called // // If notifyType == fdintNEXT_CABINET: // This function is *only* called when fdintCOPY_FILE was told to copy // a file in the current cabinet that is continued to a subsequent // cabinet file. It is important that the cabinet path name (psz3) // be validated before returning! This function should ensure that // the cabinet exists and is readable before returning. So, this // is the function that should, for example, issue a disk change // prompt and make sure the cabinet file exists // When this function returns to FDI, FDI will check that the setID // and iCabinet match the expected values for the next cabinet. // If not, FDI will continue to call this function until the correct // cabinet file is specified, or until this function returns -1 to // abort the FDICopy() function. notifyInfo->fdie is set to // FDIERROR_WRONG_CABINET to indicate this case // notifyInfo->psz1 is name of next cabinet where current file is continued // notifyInfo->psz2 is name of next disk where current file is continued // notifyInfo->psz3 is cabinet path name; FDI concatenates psz3 with psz1 // to produce the fully-qualified path for the cabinet // file. The 256-byte buffer pointed at by psz3 may // be modified, but psz1 may not! // notifyInfo->fdie is FDIERROR_WRONG_CABINET if the previous call to // fdintNEXT_CABINET specified a cabinet file that // did not match the setID/iCabinet that was expected // If you *haven't* ensured that the cabinet file is present and // readable, or the cabinet file has been damaged, notifyInfo->fdie will // receive other appropriate error codes // FDIERROR_CABINET_NOT_FOUND // FDIERROR_NOT_A_CABINET // FDIERROR_UNKNOWN_CABINET_VERSION // FDIERROR_CORRUPT_CABINET // FDIERROR_BAD_COMPR_TYPE // FDIERROR_RESERVE_MISMATCH // FDIERROR_WRONG_CABINET // This call is almost always made when a target file is open and // being written to, and the next cabinet is needed to get more // data for the file // Post : If notifyType == fdintCABINET_INFO: // If notifyType == fdintPARTIAL_FILE: // If notifyType == fdintNEXT_CABINET: // Return -1 to abort file copying // // If notifyType == fdintCLOSE_FILE_INFO: // Return 0 or -1 to abort copying // // If notifyType == fdintCOPY_FILE: // Return non-zero file handle for destination file; FDI writes // data to this file use the PFNWRITE function supplied to FDICreate, // and then calls fdintCLOSE_FILE_INFO to close the file and set // the date, time, and attributes. NOTE: This file handle returned // must also be closeable by the PFNCLOSE function supplied to // FDICreate, since if an error occurs while writing to this handle, // FDI will use the PFNCLOSE function to close the file so that the // client may delete it // Return 0 to skip file, do not copy // Return -1 to abort file copying // // If notifyType == fdintENUMERATE: // Return anything but -1 if we don't care // If we wish to force a skip, return anything but -1 and // notifyInfo->cb set to desired CFFILE position // notifyInfo->iFolder set desired # of files remaining // If we wish to stop enumeration, Return anything but -1 and // notifyInfo->iFolder set to 0 // Return -1 to abort file copying ("user aborted") // Globals : // I/O : // Task : This notification proc is called by the decompression interface 2 // inform us what it is doing with our cabinet file(s) //------------------------------------------------------------------------- int FAR DIAMONDAPI CCabinetExtractor::NotifyProgress(FDINOTIFICATIONTYPE notifyType, PFDINOTIFICATION notifyInfo) { #ifdef __TRACE_CAB_EXTRACTION__ TRACEFN(_T("CCabinetExtractor::NotifyProgress(")); #endif int result =0; BOOL ok_2_continue; CCabinetExtractor *pce =(CCabinetExtractor *)notifyInfo->pv; ASSERTX(pce); // Handle notifications differently... switch(notifyType) { case fdintCABINET_INFO: // Get general information about the cabinet #ifdef __TRACE_CAB_EXTRACTION__ TRACEX(_T("CABINET_INFO,")); TRACEX(_T("%u,%s,%s,%s,%u)\n"),notifyInfo->setID,notifyInfo->psz3,notifyInfo->psz1,notifyInfo->psz2,notifyInfo->iCabinet); #endif { // Notifying client... ok_2_continue =pce->NotifyCabinetInfo(notifyInfo->setID, // ID of cabinet set notifyInfo->psz3, // Cabinet path notifyInfo->psz1, // Cabinet name (might be empty) notifyInfo->psz2, // Disk name (might be empty) notifyInfo->iCabinet); // Index of cabinet being processed (the one used in ExtractFiles) (0-based) result =ok_2_continue ? 0 : -1; } break; case fdintCOPY_FILE: // File to be copied #ifdef __TRACE_CAB_EXTRACTION__ TRACEX(_T("COPY_FILE,")); TRACEX(_T("%s,%lu,0x%04x,0x%04x,0x%04x)...\n"),notifyInfo->psz1,notifyInfo->cb,notifyInfo->date,notifyInfo->time,notifyInfo->attribs); #endif { pce->m_bLastFileSkipped =FALSE; // Notifying client... string strExtractTo; BOOL bSkipFile =FALSE; ok_2_continue =pce->NotifyFileCopy(notifyInfo->psz1, // File name notifyInfo->cb, // File size uncompressed strExtractTo, // Extract name (client fills this in) bSkipFile); // Whether 2 extract or skip (client fills this in) result =ok_2_continue ? (bSkipFile ? 0 : 1) : -1; // Check if client wants 2 copy this file somewhere or // requested abort copying if(ok_2_continue) { // Check if client returned a path where 2 extract this file // If yes, we'll extract there otherwise we will skip this file if(!bSkipFile && !strExtractTo.empty()) { // Create target file TCHAR temp_path[MAX_PATH]; STRCPY(temp_path,strExtractTo.c_str()); #ifdef __TRACE_CAB_EXTRACTION__ TRACEX(_T("Extracting as %s\n"),strExtractTo.c_str()); #endif SetFileAttributes(temp_path,FILE_ATTRIBUTE_NORMAL); result =FileOpen(temp_path,_O_BINARY | _O_CREAT | _O_WRONLY | _O_SEQUENTIAL,_S_IREAD | _S_IWRITE); pce->m_LastExtracted =strExtractTo.c_str(); // Remeber the cabinet index where this file starts // We always remember the start/end cabinet index of the last // file being extracted, so that we can determine if the // file spans across cabinet boundaries pce->m_nLastFileStartCabinetIndex =pce->m_nNextIndex; if(!pce->m_bCabinetSetFinished) (pce->m_nLastFileStartCabinetIndex)--; } else { // Force skipping this file because no extract path // was specified result =0; pce->m_bLastFileSkipped =TRUE; #ifdef __TRACE_CAB_EXTRACTION__ TRACEX(_T("Skipped\n")); #endif } } else { #ifdef __TRACE_CAB_EXTRACTION__ TRACEX(_T("Aborted\n")); #endif } } break; case fdintCLOSE_FILE_INFO: // Close the file, set relevant info #ifdef __TRACE_CAB_EXTRACTION__ TRACEX(_T("CLOSE_FILE_INFO,")); TRACEX(_T("%s,0x%04x,0x%04x,0x%04x)\n"),notifyInfo->psz1,notifyInfo->date,notifyInfo->time,notifyInfo->attribs); #endif { // Notifying client... ok_2_continue =pce->NotifyFileCopied(notifyInfo->psz1, // File name notifyInfo->date, // File date notifyInfo->time, // File time notifyInfo->attribs); // File attributes result =ok_2_continue ? 1 : -1; // Check if client aborted copying if(ok_2_continue) { // Close file FileClose(notifyInfo->hf); // Set file date/time string strFullExractName =(LPCTSTR)pce->m_LastExtracted; HANDLE handle =CreateFile(strFullExractName.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(handle != INVALID_HANDLE_VALUE) { FILETIME datetime; FILETIME datetime_local; DosDateTimeToFileTime(notifyInfo->date,notifyInfo->time,&datetime); LocalFileTimeToFileTime(&datetime,&datetime_local); SetFileTime(handle,&datetime_local,NULL,&datetime_local); CloseHandle(handle); } // Set file attributes // Mask out attribute bits other than read-only, // hidden, system, and archive, since the other // attribute bits are reserved for use by the cabinet format DWORD attributes =notifyInfo->attribs & (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH); SetFileAttributes(strFullExractName.c_str(),attributes); // Remeber the cabinet index where this file ends // We always remember the start/end cabinet index of the last // file being extracted, so that we can determine if the // file spans across cabinet boundaries pce->m_nLastFileEndCabinetIndex =pce->m_nNextIndex; if(!pce->m_bCabinetSetFinished) (pce->m_nLastFileEndCabinetIndex)--; // Now also notify clients that file is ready 4 manipulation ok_2_continue =pce->NotifyFileCopiedAndClosed(notifyInfo->psz1); // File name if(!ok_2_continue) result =-1; } } break; case fdintPARTIAL_FILE: // First file in cabinet is continuation #ifdef __TRACE_CAB_EXTRACTION__ TRACEX(_T("PARTIAL_FILE,")); TRACEX(_T("%s,%s,%s)\n"),notifyInfo->psz1,notifyInfo->psz2,notifyInfo->psz3); #endif { // Notifying client... ok_2_continue =pce->NotifyPartialFile(notifyInfo->psz1, // File name notifyInfo->psz2, // Name of cabinet where file starts notifyInfo->psz3); // Name of disk where file starts result =ok_2_continue ? 0 : -1; } break; case fdintNEXT_CABINET: // File continued to next cabinet #ifdef __TRACE_CAB_EXTRACTION__ TRACEX(_T("NEXT_CABINET,")); TRACEX(_T("%s,%s,%s,%d)\n"),notifyInfo->psz1,notifyInfo->psz2,notifyInfo->psz3,notifyInfo->fdie); #endif { // Notifying client... string strPathToNextCabinet(notifyInfo->psz3); ok_2_continue =pce->NotifyNextCabinet(notifyInfo->psz1, // Name of next cabinet notifyInfo->psz2, // Name of next disk strPathToNextCabinet, // Path 2 next cabinet (client fills this in) notifyInfo->fdie); // Last error code STRNCPY(notifyInfo->psz3,strPathToNextCabinet.c_str(),MAX_PATH); result =ok_2_continue ? 0 : -1; } break; case fdintENUMERATE: // Enumerating files #ifdef __TRACE_CAB_EXTRACTION__ TRACEX(_T("ENUMERATE,")); TRACEX(_T("%u,%lu,%u)\n"),notifyInfo->setID,notifyInfo->cb,notifyInfo->iFolder); #endif { ok_2_continue =pce->NotifyEnumerate(notifyInfo->setID, // Cabinet set ID notifyInfo->cb, // Current CFFILE position notifyInfo->iFolder); // Number of files remaining result =ok_2_continue ? 0 : -1; } break; } return result; } #endif #ifdef __CAB_EXTRACT__ //------------------------------------------------------------------------- // Pre : // Post : // Globals : // I/O : // Task : Delete and cleanup a cabinet extractor object //------------------------------------------------------------------------- CCabinetExtractor::~CCabinetExtractor() { #ifdef __TRACE_CAB_EXTRACTION__ TRACEFN(_T("CCabinetExtractor::~CCabinetExtractor\n")); #endif // Free the decompression context (if any) if(m_bStatus && (m_hContext != NULL)) FDIDestroy(m_hContext); } #endif