|
|
//=======================================================================
//
// Copyright (c) 1998-1999 Microsoft Corporation. All Rights Reserved.
//
// File: diamond.cpp
//
// Owner: YanL
//
// Description:
//
// Diamond decompressor implementation
//
//=======================================================================
#include <windows.h>
#include <shlwapi.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <tchar.h>
#include <atlconv.h>
#include <wustl.h>
//#define LOGGING_LEVEL 3
//#include <log.h>
#include <diamond.h>
//Note: The use of statics means that this code is not thread safe. It also means that
//this code can only perform 1 decompression at a time. I think that I may need to
//rewrite this to be callable from multiple places as it may end up that CDM and
//Active Setup both need to access this class at the same time.
CHInfoArray CDiamond::s_handles; byte_buffer* CDiamond::s_pbufIn = NULL; byte_buffer* CDiamond::s_pbufOut = NULL;
CDiamond::CDiamond( void ) : m_hfdi(0) { ZeroMemory(&m_erf, sizeof(ERF));
m_hfdi = FDICreate(DEXMemAlloc, DEXMemFree, DEXFileOpen, DEXFileRead, DEXFileWrite, DEXFileClose, DEXFileSeek, cpu80386, &m_erf); }
CDiamond::~CDiamond( void ) {
if (NULL != m_hfdi) FDIDestroy(m_hfdi); }
bool CDiamond::IsValidCAB ( IN byte_buffer& bufIn // Mem buffer in
) { //LOG_block("CDiamond::IsValidCAB");
s_pbufIn = &bufIn;
int hf = DEXFileOpen("?", _O_RDWR, 0); if ((INT_PTR)INVALID_HANDLE_VALUE == hf) return FALSE;
FDICABINETINFO fdici; bool bRc = TRUE == FDIIsCabinet(m_hfdi, hf, &fdici);
DEXFileClose(hf);
return bRc; }
bool CDiamond::IsValidCAB( IN LPCTSTR szCabPath ) { //LOG_block("CDiamond::IsValidCAB");
USES_CONVERSION;
int hf = DEXFileOpen(T2A(const_cast<TCHAR*>(szCabPath)), _O_RDWR, 0); if ((INT_PTR)INVALID_HANDLE_VALUE == hf) return FALSE;
FDICABINETINFO fdici; bool bRc = TRUE == FDIIsCabinet(m_hfdi, hf, &fdici);
DEXFileClose(hf);
return bRc; }
bool CDiamond::Decompress( IN LPCTSTR szFileIn, // Full path to input cab file.
IN LPCTSTR szFileOut ) { SetInput(szFileIn); SetOutput(szFileOut); return DoDecompress(); }
bool CDiamond::Decompress( IN LPCTSTR szFileIn, // Full path to input cab file.
IN byte_buffer& bufOut // Mem buffer out
) { SetInput(szFileIn); SetOutput(bufOut); return DoDecompress(); }
bool CDiamond::Decompress( IN byte_buffer& bufIn, // Mem buffer in
IN LPCTSTR szFileOut ) { SetInput(bufIn); SetOutput(szFileOut); return DoDecompress(); }
bool CDiamond::Decompress( IN byte_buffer& bufIn, // Mem buffer in
IN byte_buffer& bufOut // Mem buffer out
) { SetInput(bufIn); SetOutput(bufOut); return DoDecompress(); }
bool CDiamond::DoDecompress( void ) { //LOG_block("CDiamond::DoDecompress");
USES_CONVERSION;
const static TCHAR szQuestion[] = _T("?");
TCHAR szCabinet[MAX_PATH] = {0}; TCHAR szCabPath[MAX_PATH] = {0}; if (NULL != m_szFileIn) { lstrcpy(szCabinet, PathFindFileName(m_szFileIn)); lstrcpy(szCabPath, m_szFileIn); PathRemoveFileSpec(szCabPath); PathAddBackslash(szCabPath); } else { lstrcpy(szCabinet, szQuestion); }
if (NULL == m_szFileOut) m_szFileOut = szQuestion;
//LOG_out("FDICopy(szCabinet= %s, szCabPath = %s)", szCabinet, szCabPath);
return TRUE == FDICopy(m_hfdi, T2A(szCabinet), T2A(szCabPath), 0, Notification, NULL, this); }
void *DIAMONDAPI CDiamond::DEXMemAlloc( ULONG cb ) { return malloc(cb); }
void DIAMONDAPI CDiamond::DEXMemFree( void HUGE *pv ) { free(pv); }
BOOL CreatePath(LPCTSTR pszPath) { if (CreateDirectory(pszPath, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) return TRUE; TCHAR* pcLastSlash = _tcsrchr(pszPath, _T('\\')); if (NULL == pcLastSlash) return FALSE; *pcLastSlash = 0; if (!CreatePath(pszPath)) return FALSE; *pcLastSlash = _T('\\'); return CreateDirectory(pszPath, NULL); }
INT_PTR DIAMONDAPI CDiamond::DEXFileOpen( char *pszFile, int oflag, int pmode ) {
USES_CONVERSION;
HANDLEINFO hinfo; ZeroMemory(&hinfo, sizeof(HANDLEINFO));
//if the file name begins with an invalid ? character then we have
//an in memory operation.
hinfo.offset = 0L; //Set memory file pointer to beginning of file
hinfo.handle = INVALID_HANDLE_VALUE; // It will stay that way if it's in memory operation
if ('?' != pszFile[0]) // if not in memory op
{ if (oflag & _O_CREAT) { // file to be opened for write.
// Make sure that path exists
TCHAR szPath[MAX_PATH]; lstrcpy(szPath, A2T(pszFile)); PathRemoveFileSpec(szPath); CreatePath(szPath); hinfo.handle = CreateFile(A2T(pszFile), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); } else { //file to be opened for reading.
hinfo.handle = CreateFile(A2T(pszFile), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); } if (hinfo.handle == INVALID_HANDLE_VALUE) return (INT_PTR)INVALID_HANDLE_VALUE; } return s_handles.add(hinfo) + 1; }
UINT DIAMONDAPI CDiamond::DEXFileRead( INT_PTR hf, void *pv, UINT cb ) { //LOG_block("CDiamond::DEXFileRead");
HANDLEINFO& rhinfo = s_handles[hf-1]; ULONG cnLength = -1;
//if this is a file read operation use normal 32 bit I/O
if (rhinfo.handle == INVALID_HANDLE_VALUE) { // we have an in memory operation and need to copy the requested data.
//Note: while we would like to avoid even this memory copy we can't since
//the diamond libary is allocating an internal read buffer.
byte_buffer& rbuf = *s_pbufIn; if (rhinfo.offset + cb < rbuf.size()) cnLength = cb; else cnLength = rbuf.size() - rhinfo.offset;
if ((int)cnLength > 0) { memcpy(pv, (LPBYTE)rbuf + rhinfo.offset, cnLength); rhinfo.offset += cnLength; } else { cnLength = 0; } } else { // this is a file read operation use normal 32 bit I/O
(void)ReadFile(rhinfo.handle, pv, cb, &cnLength, NULL); //if( !ReadFile(rhinfo.handle, pv, cb, &cnLength, NULL))
// LOG_error("ReadFile - GetLastError() = %d", ::GetLastError());
}
return cnLength; }
UINT DIAMONDAPI CDiamond::DEXFileWrite( INT_PTR hf, void *pv, UINT cb ) { //LOG_block("CDiamond::DEXFileWrite");
HANDLEINFO& rhinfo = s_handles[hf-1]; ULONG cnLength = -1;
if (rhinfo.handle == INVALID_HANDLE_VALUE) { //Since we can dynamically increase the output buffer array and also
//access it in the normal C++ way though a pointer writes are very easy
//we simply need to make sure that there is sufficient space in the
//output buffer, get a pointer to the current file offset and memcpy
//the new data into the file buffer.
byte_buffer& rbuf = *s_pbufOut; if (rbuf.size() < rhinfo.offset + cb) { rbuf.resize(rhinfo.offset + cb); if (!rbuf.valid()) return 0; } memcpy((LPBYTE)rbuf + rhinfo.offset, pv, cb);
rhinfo.offset += cb;
cnLength = cb; } else { // this is a file write operation use normal 32 bit I/O
(void)WriteFile(rhinfo.handle, pv, cb, &cnLength, NULL); //if( !WriteFile(rhinfo.handle, pv, cb, &cnLength, NULL))
//LOG_error("WriteFile - GetLastError() = %d", ::GetLastError());
} return cnLength; }
long DIAMONDAPI CDiamond::DEXFileSeek( INT_PTR hf, long dist, int seektype ) {
HANDLEINFO& rhinfo = s_handles[hf-1];
//Since we are using 32 bit file io we need to remap the seek method
DWORD dwMoveMethod; switch (seektype) { case SEEK_CUR: dwMoveMethod = FILE_CURRENT; break; case SEEK_END: dwMoveMethod = FILE_END; break; case SEEK_SET: default: dwMoveMethod = FILE_BEGIN; break; }
//if this is a file read operation use normal 32 bit I/O
if (rhinfo.handle != INVALID_HANDLE_VALUE) return SetFilePointer(rhinfo.handle, dist, NULL, dwMoveMethod);
//else we need to interpret the seek based on the type
switch (dwMoveMethod) { case FILE_CURRENT: rhinfo.offset += dist; break; case FILE_END: rhinfo.offset -= dist; break; case FILE_BEGIN: default: rhinfo.offset = dist; break; }
return rhinfo.offset; }
int DIAMONDAPI CDiamond::DEXFileClose( INT_PTR hf ) {
HANDLEINFO& rhinfo = s_handles[hf-1];
//if this is a file operation close the file handle and make the
//internal handle structure available for use.
if (rhinfo.handle != INVALID_HANDLE_VALUE) { CloseHandle(rhinfo.handle); }
//If this is a write buffer then we exit and let the Decompress
//function take care of allocating and copying the data back
//to the caller if necessary.
s_handles.remove(hf-1); return 0; }
INT_PTR __cdecl CDiamond::Notification( FDINOTIFICATIONTYPE fdiNotifType, PFDINOTIFICATION pfdin ) {
USES_CONVERSION;
switch (fdiNotifType) { case fdintCABINET_INFO: // general information about the cabinet
return 0; case fdintNEXT_CABINET: // file continued to next cabinet
case fdintPARTIAL_FILE: // first file in cabinet is continuation
return 0; case fdintCOPY_FILE: // file to be copied, returns 0 = skip, -1 = abort, else file handle
case fdintCLOSE_FILE_INFO: break; default: return 0; }
CDiamond *pDiamond = (CDiamond *)pfdin->pv;
// prepare output file name
TCHAR szFileOut[MAX_PATH]; if (pDiamond->m_szFileOut[0] == _T('*')) { //Special case where the caller has asked for all files in the cab to
//be expanded. In this case we need to construct an output file name
//for this imput file.
lstrcpy(szFileOut, pDiamond->m_szFileIn); PathRemoveFileSpec(szFileOut); PathAppend(szFileOut, A2T(pfdin->psz1)); } else if (StrChr(pDiamond->m_szFileOut, _T('*'))) { lstrcpy(szFileOut, pDiamond->m_szFileOut); PathRemoveFileSpec(szFileOut); PathAppend(szFileOut, A2T(pfdin->psz1)); } else { lstrcpy(szFileOut, pDiamond->m_szFileOut); }
// two cases we want to do
if (fdintCOPY_FILE == fdiNotifType) { return pDiamond->DEXFileOpen(T2A(szFileOut), _O_CREAT | _O_BINARY | _O_TRUNC | _O_RDWR, _S_IREAD | _S_IWRITE);
} else // fdintCLOSE_FILE_INFO == fdiNotifType
{ pDiamond->DEXFileClose(pfdin->hf); if( '?' != pfdin->psz1[0]) { auto_hfile hFile = CreateFile( szFileOut, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (hFile.valid()) { FILETIME ftLocal; FILETIME ftUTC; SetFileAttributes(szFileOut, FILE_ATTRIBUTE_NORMAL); if (DosDateTimeToFileTime(pfdin->date, pfdin->time, &ftLocal) && LocalFileTimeToFileTime(&ftLocal, &ftUTC)) { SetFileTime(hFile, NULL, NULL, &ftUTC); } } } return 1; } }
|