578 lines
11 KiB
C++
578 lines
11 KiB
C++
|
//
|
||
|
// MODULE: CABUNCOMPRESS.CPP
|
||
|
//
|
||
|
// PURPOSE: CAB File Support Class
|
||
|
//
|
||
|
// PROJECT: Generic Troubleshooter DLL for Microsoft AnswerPoint
|
||
|
//
|
||
|
// COMPANY: Saltmine Creative, Inc. (206)-284-7511 support@saltmine.com
|
||
|
//
|
||
|
// AUTHOR: Richard Meadows
|
||
|
//
|
||
|
// ORIGINAL DATE: 8/7/97
|
||
|
//
|
||
|
// NOTES:
|
||
|
// 1.
|
||
|
//
|
||
|
// Version Date By Comments
|
||
|
//--------------------------------------------------------------------
|
||
|
// V0.2 6/4/97 RM Local Version for Memphis
|
||
|
// V0.3 04/09/98 JM/OK+ Local Version for NT5
|
||
|
//
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <io.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <dos.h>
|
||
|
#include <sys/stat.h>
|
||
|
|
||
|
#include "CabUnCompress.h"
|
||
|
|
||
|
#include "chmread.h"
|
||
|
#include "apgts.h"
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
#ifndef _UNICODE
|
||
|
#define PRINT_OUT ::AfxTrace
|
||
|
//#define PRINT_OUT 1 ? (void)0 : ::AfxTrace
|
||
|
#else
|
||
|
#define PRINT_OUT 1 ? (void)0 : ::AfxTrace
|
||
|
#endif
|
||
|
#else
|
||
|
#define PRINT_OUT 1 ? (void)0 : ::AfxTrace
|
||
|
#endif
|
||
|
|
||
|
// Need this to compile unicode builds.
|
||
|
bool TcharToChar(char szOut[], LPCTSTR szIn, int &OutLen)
|
||
|
{
|
||
|
int x = 0;
|
||
|
while(NULL != szIn[x] && x < OutLen)
|
||
|
{
|
||
|
szOut[x] = (char) szIn[x];
|
||
|
x++;
|
||
|
}
|
||
|
if (x < OutLen)
|
||
|
szOut[x] = NULL;
|
||
|
return x < OutLen;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Call back functions needed to use the fdi library.
|
||
|
|
||
|
/*
|
||
|
* Memory allocation function
|
||
|
*/
|
||
|
FNALLOC(mem_alloc)
|
||
|
{
|
||
|
return malloc(cb);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Memory free function
|
||
|
*/
|
||
|
FNFREE(mem_free)
|
||
|
{
|
||
|
free(pv);
|
||
|
}
|
||
|
|
||
|
|
||
|
FNOPEN(file_open)
|
||
|
{
|
||
|
return _open(pszFile, oflag, pmode);
|
||
|
}
|
||
|
|
||
|
|
||
|
FNREAD(file_read)
|
||
|
{
|
||
|
return _read(hf, pv, cb);
|
||
|
}
|
||
|
|
||
|
|
||
|
FNWRITE(file_write)
|
||
|
{
|
||
|
return _write(hf, pv, cb);
|
||
|
}
|
||
|
|
||
|
|
||
|
FNCLOSE(file_close)
|
||
|
{
|
||
|
return _close(hf);
|
||
|
}
|
||
|
|
||
|
|
||
|
FNSEEK(file_seek)
|
||
|
{
|
||
|
return _lseek(hf, dist, seektype);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Function prototypes
|
||
|
*/
|
||
|
BOOL test_fdi(TCHAR *cabinet_file);
|
||
|
int get_percentage(unsigned long a, unsigned long b);
|
||
|
TCHAR *return_fdi_error_string(FDIERROR err);
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Destination directory for extracted files
|
||
|
*/
|
||
|
char dest_dir[MAX_PATH];
|
||
|
|
||
|
// Last file to be extracted.
|
||
|
char last_extracted[MAX_PATH];
|
||
|
|
||
|
FNFDINOTIFY(notification_function)
|
||
|
{
|
||
|
switch (fdint)
|
||
|
{
|
||
|
case fdintCABINET_INFO: // general information about the cabinet
|
||
|
PRINT_OUT(
|
||
|
_T("fdintCABINET_INFO\n")
|
||
|
_T(" next cabinet = %s\n")
|
||
|
_T(" next disk = %s\n")
|
||
|
_T(" cabinet path = %s\n")
|
||
|
_T(" cabinet set ID = %d\n")
|
||
|
_T(" cabinet # in set = %d (zero based)\n")
|
||
|
_T("\n"),
|
||
|
pfdin->psz1,
|
||
|
pfdin->psz2,
|
||
|
pfdin->psz3,
|
||
|
pfdin->setID,
|
||
|
pfdin->iCabinet);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
case fdintPARTIAL_FILE: // first file in cabinet is continuation
|
||
|
PRINT_OUT(
|
||
|
_T("fdintPARTIAL_FILE\n")
|
||
|
_T(" name of continued file = %s\n")
|
||
|
_T(" name of cabinet where file starts = %s\n")
|
||
|
_T(" name of disk where file starts = %s\n"),
|
||
|
pfdin->psz1,
|
||
|
pfdin->psz2,
|
||
|
pfdin->psz3);
|
||
|
return 0;
|
||
|
|
||
|
case fdintCOPY_FILE: // file to be copied
|
||
|
{
|
||
|
int handle;
|
||
|
char destination[MAX_PATH];
|
||
|
|
||
|
PRINT_OUT(
|
||
|
_T("fdintCOPY_FILE\n")
|
||
|
_T(" file name in cabinet = %s\n")
|
||
|
_T(" uncompressed file size = %d\n")
|
||
|
_T(" copy this file? (y/n): y"),
|
||
|
pfdin->psz1,
|
||
|
pfdin->cb);
|
||
|
|
||
|
strcpy(last_extracted, pfdin->psz1);
|
||
|
|
||
|
PRINT_OUT(_T("\n"));
|
||
|
|
||
|
sprintf(
|
||
|
destination,
|
||
|
"%s%s",
|
||
|
dest_dir,
|
||
|
pfdin->psz1
|
||
|
);
|
||
|
|
||
|
handle = file_open(
|
||
|
destination,
|
||
|
_O_BINARY | _O_CREAT | _O_TRUNC | _O_WRONLY | _O_SEQUENTIAL,
|
||
|
_S_IREAD | _S_IWRITE
|
||
|
);
|
||
|
|
||
|
return handle;
|
||
|
}
|
||
|
|
||
|
case fdintCLOSE_FILE_INFO: // close the file, set relevant info
|
||
|
{
|
||
|
HANDLE handle;
|
||
|
DWORD attrs;
|
||
|
char destination[MAX_PATH];
|
||
|
|
||
|
PRINT_OUT(
|
||
|
_T("fdintCLOSE_FILE_INFO\n")
|
||
|
_T(" file name in cabinet = %s\n")
|
||
|
_T("\n"),
|
||
|
pfdin->psz1);
|
||
|
|
||
|
sprintf(
|
||
|
destination,
|
||
|
"%s%s",
|
||
|
dest_dir,
|
||
|
pfdin->psz1);
|
||
|
|
||
|
file_close(pfdin->hf);
|
||
|
|
||
|
/*
|
||
|
* Set date/time
|
||
|
*
|
||
|
* Need Win32 type handle for to set date/time
|
||
|
*/
|
||
|
handle = CreateFileA(
|
||
|
destination,
|
||
|
GENERIC_READ | GENERIC_WRITE,
|
||
|
FILE_SHARE_READ,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
FILE_ATTRIBUTE_NORMAL,
|
||
|
NULL);
|
||
|
|
||
|
if (handle != INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
FILETIME datetime;
|
||
|
|
||
|
if (TRUE == DosDateTimeToFileTime(
|
||
|
pfdin->date,
|
||
|
pfdin->time,
|
||
|
&datetime))
|
||
|
{
|
||
|
FILETIME local_filetime;
|
||
|
|
||
|
if (TRUE == LocalFileTimeToFileTime(
|
||
|
&datetime,
|
||
|
&local_filetime))
|
||
|
{
|
||
|
(void) SetFileTime(
|
||
|
handle,
|
||
|
&local_filetime,
|
||
|
NULL,
|
||
|
&local_filetime);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CloseHandle(handle);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Mask out attribute bits other than readonly,
|
||
|
* hidden, system, and archive, since the other
|
||
|
* attribute bits are reserved for use by
|
||
|
* the cabinet format.
|
||
|
*/
|
||
|
attrs = pfdin->attribs;
|
||
|
|
||
|
attrs &= (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH);
|
||
|
|
||
|
(void) SetFileAttributesA(
|
||
|
destination,
|
||
|
attrs);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
case fdintNEXT_CABINET: // file continued to next cabinet
|
||
|
PRINT_OUT(
|
||
|
_T("fdintNEXT_CABINET\n")
|
||
|
_T(" name of next cabinet where file continued = %s\n")
|
||
|
_T(" name of next disk where file continued = %s\n")
|
||
|
_T(" cabinet path name = %s\n")
|
||
|
_T("\n"),
|
||
|
pfdin->psz1,
|
||
|
pfdin->psz2,
|
||
|
pfdin->psz3);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
TCHAR *return_fdi_error_string(FDIERROR err)
|
||
|
{
|
||
|
switch (err)
|
||
|
{
|
||
|
case FDIERROR_NONE:
|
||
|
return _T("No error");
|
||
|
|
||
|
case FDIERROR_CABINET_NOT_FOUND:
|
||
|
return _T("Cabinet not found");
|
||
|
|
||
|
case FDIERROR_NOT_A_CABINET:
|
||
|
return _T("Not a cabinet");
|
||
|
|
||
|
case FDIERROR_UNKNOWN_CABINET_VERSION:
|
||
|
return _T("Unknown cabinet version");
|
||
|
|
||
|
case FDIERROR_CORRUPT_CABINET:
|
||
|
return _T("Corrupt cabinet");
|
||
|
|
||
|
case FDIERROR_ALLOC_FAIL:
|
||
|
return _T("Memory allocation failed");
|
||
|
|
||
|
case FDIERROR_BAD_COMPR_TYPE:
|
||
|
return _T("Unknown compression type");
|
||
|
|
||
|
case FDIERROR_MDI_FAIL:
|
||
|
return _T("Failure decompressing data");
|
||
|
|
||
|
case FDIERROR_TARGET_FILE:
|
||
|
return _T("Failure writing to target file");
|
||
|
|
||
|
case FDIERROR_RESERVE_MISMATCH:
|
||
|
return _T("Cabinets in set have different RESERVE sizes");
|
||
|
|
||
|
case FDIERROR_WRONG_CABINET:
|
||
|
return _T("Cabinet returned on fdintNEXT_CABINET is incorrect");
|
||
|
|
||
|
case FDIERROR_USER_ABORT:
|
||
|
return _T("User aborted");
|
||
|
|
||
|
default:
|
||
|
return _T("Unknown error");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CCabUnCompress::CCabUnCompress()
|
||
|
{
|
||
|
m_strError = _T("");
|
||
|
m_nError = NO_ERROR;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
BOOL CCabUnCompress::ExtractCab(CString &strCabFile, CString &strDestDir, const CString& strFile)
|
||
|
{
|
||
|
HFDI hfdi;
|
||
|
ERF erf;
|
||
|
FDICABINETINFO fdici;
|
||
|
int hf;
|
||
|
char *p;
|
||
|
char cabinet_name[MAX_PATH];
|
||
|
char cabinet_path[MAX_PATH];
|
||
|
bool bUseCHM = strFile.GetLength() != 0;
|
||
|
BOOL bRet = FALSE;
|
||
|
BOOL bWasRenamed = FALSE;
|
||
|
|
||
|
char sznCabFile[MAX_PATH];
|
||
|
char sznDestDir[MAX_PATH * 3];
|
||
|
int Len = MAX_PATH;
|
||
|
TcharToChar(sznCabFile, (LPCTSTR) strCabFile, Len);
|
||
|
Len = MAX_PATH * 3;
|
||
|
TcharToChar(sznDestDir, (LPCTSTR) strDestDir, Len);
|
||
|
|
||
|
|
||
|
ASSERT(strDestDir.GetLength() < MAX_PATH);
|
||
|
strcpy(dest_dir, sznDestDir);
|
||
|
|
||
|
hfdi = FDICreate(
|
||
|
mem_alloc,
|
||
|
mem_free,
|
||
|
file_open,
|
||
|
file_read,
|
||
|
file_write,
|
||
|
file_close,
|
||
|
file_seek,
|
||
|
cpu80386,
|
||
|
&erf
|
||
|
);
|
||
|
|
||
|
if (hfdi == NULL)
|
||
|
{
|
||
|
m_strError.Format(_T("FDICreate() failed: code %d [%s]\n"),
|
||
|
erf.erfOper, return_fdi_error_string(erf.erfOper));
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (bUseCHM)
|
||
|
{
|
||
|
/*
|
||
|
* If strCabFile is CHM file - extract data from *.dsz file inside CHM
|
||
|
* and save this data in strDestDir directory as temperary file
|
||
|
* It means that we are copying *.dsz file in temp directory, then
|
||
|
* decode it to *.dsc file.
|
||
|
* *.dsz file fill be removed in this function, *.dsc file will be renoved
|
||
|
* later.
|
||
|
*/
|
||
|
|
||
|
// modify sznCabFile from path\*.chm to temp_path\network.dsz
|
||
|
strcpy(sznCabFile, strDestDir);
|
||
|
strcat(sznCabFile, strFile);
|
||
|
|
||
|
hf = file_open(
|
||
|
sznCabFile,
|
||
|
_O_CREAT | _O_TRUNC | /*_O_TEMPORARY |*/
|
||
|
_O_BINARY | _O_RDWR | _O_SEQUENTIAL ,
|
||
|
_S_IREAD | _S_IWRITE
|
||
|
);
|
||
|
|
||
|
if (hf != -1)
|
||
|
{
|
||
|
// write in temp file now
|
||
|
void* buf =NULL;
|
||
|
DWORD size =0;
|
||
|
|
||
|
if (S_OK == ::ReadChmFile(strCabFile, strFile, &buf, &size))
|
||
|
{
|
||
|
int ret = _write(hf, buf, size);
|
||
|
delete [] buf;
|
||
|
if (-1 == ret)
|
||
|
{
|
||
|
FDIDestroy(hfdi);
|
||
|
_close(hf);
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
FDIDestroy(hfdi);
|
||
|
_close(hf);
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
FDIDestroy(hfdi);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
_close(hf);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Is this file really a cabinet?
|
||
|
*/
|
||
|
hf = file_open(
|
||
|
sznCabFile,
|
||
|
_O_BINARY | _O_RDONLY | _O_SEQUENTIAL,
|
||
|
0
|
||
|
);
|
||
|
|
||
|
if (hf == -1)
|
||
|
{
|
||
|
(void) FDIDestroy(hfdi);
|
||
|
|
||
|
m_strError.Format(_T("Unable to open '%s' for input\n"), (LPCTSTR) strCabFile);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
bRet = FDIIsCabinet(hfdi,
|
||
|
hf,
|
||
|
&fdici);
|
||
|
|
||
|
_close(hf);
|
||
|
|
||
|
if (FALSE == bRet)
|
||
|
{
|
||
|
/*
|
||
|
* No, it's not a cabinet!
|
||
|
*/
|
||
|
if (bUseCHM)
|
||
|
{
|
||
|
// But if we were using CHM -
|
||
|
// we have extracted this *.dsz (that has *.dsc format)
|
||
|
// in TEMP directory and all we heed - just rename it to
|
||
|
// *.dsc.
|
||
|
// This would EMULATE case where we have *.dsz compressed.
|
||
|
CString strUncompressedFile, strCabFile(sznCabFile);
|
||
|
strUncompressedFile = strCabFile.Left(strCabFile.GetLength() - 4);
|
||
|
strUncompressedFile += DSC_UNCOMPRESSED;
|
||
|
remove(strUncompressedFile); // remove if exists
|
||
|
if (0 != rename(strCabFile, strUncompressedFile))
|
||
|
{
|
||
|
FDIDestroy(hfdi);
|
||
|
goto AWAY;
|
||
|
}
|
||
|
CString strJustUncompressedFileName = ::ExtractFileName(strUncompressedFile);
|
||
|
strcpy(last_extracted, strJustUncompressedFileName);
|
||
|
bWasRenamed = TRUE;
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_strError.Format(
|
||
|
_T("FDIIsCabinet() failed: '%s' is not a cabinet\n"),
|
||
|
(LPCTSTR) strCabFile);
|
||
|
m_nError = NOT_A_CAB;
|
||
|
}
|
||
|
(void) FDIDestroy(hfdi);
|
||
|
goto AWAY;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PRINT_OUT(
|
||
|
_T("Information on cabinet file '%s'\n")
|
||
|
_T(" Total length of cabinet file : %d\n")
|
||
|
_T(" Number of folders in cabinet : %d\n")
|
||
|
_T(" Number of files in cabinet : %d\n")
|
||
|
_T(" Cabinet set ID : %d\n")
|
||
|
_T(" Cabinet number in set : %d\n")
|
||
|
_T(" RESERVE area in cabinet? : %s\n")
|
||
|
_T(" Chained to prev cabinet? : %s\n")
|
||
|
_T(" Chained to next cabinet? : %s\n")
|
||
|
_T("\n"),
|
||
|
(LPCTSTR) strCabFile,
|
||
|
fdici.cbCabinet,
|
||
|
fdici.cFolders,
|
||
|
fdici.cFiles,
|
||
|
fdici.setID,
|
||
|
fdici.iCabinet,
|
||
|
fdici.fReserve == TRUE ? _T("yes") : _T("no"),
|
||
|
fdici.hasprev == TRUE ? _T("yes") : _T("no"),
|
||
|
fdici.hasnext == TRUE ? _T("yes") : _T("no")
|
||
|
);
|
||
|
}
|
||
|
|
||
|
p = strchr(sznCabFile, '\\');
|
||
|
|
||
|
if (p == NULL)
|
||
|
{
|
||
|
strcpy(cabinet_name, sznCabFile);
|
||
|
strcpy(cabinet_path, "");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
strcpy(cabinet_name, p+1);
|
||
|
|
||
|
char *pCab = sznCabFile;
|
||
|
|
||
|
strncpy(cabinet_path, sznCabFile, (int) (p-pCab)+1);
|
||
|
cabinet_path[ (int) (p-pCab)+1 ] = 0;
|
||
|
}
|
||
|
|
||
|
if (TRUE != FDICopy(
|
||
|
hfdi,
|
||
|
cabinet_name,
|
||
|
cabinet_path,
|
||
|
0,
|
||
|
notification_function,
|
||
|
NULL,
|
||
|
NULL))
|
||
|
{
|
||
|
m_strError.Format(
|
||
|
_T("FDICopy() failed: code %d [%s]\n"),
|
||
|
erf.erfOper, return_fdi_error_string(erf.erfOper));
|
||
|
|
||
|
(void) FDIDestroy(hfdi);
|
||
|
bRet = FALSE;
|
||
|
goto AWAY;
|
||
|
}
|
||
|
|
||
|
if (FDIDestroy(hfdi) != TRUE)
|
||
|
{
|
||
|
m_strError.Format(
|
||
|
_T("FDIDestroy() failed: code %d [%s]\n"),
|
||
|
erf.erfOper, return_fdi_error_string(erf.erfOper));
|
||
|
|
||
|
bRet = FALSE;
|
||
|
goto AWAY;
|
||
|
}
|
||
|
|
||
|
AWAY:
|
||
|
if (bUseCHM && !bWasRenamed)
|
||
|
remove(sznCabFile);
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
CString CCabUnCompress::GetLastFile()
|
||
|
{
|
||
|
CString str = last_extracted;
|
||
|
return str;
|
||
|
}
|