windows-nt/Source/XPSP1/NT/enduser/windows.com/wuv3/cdmlib/diamond.cpp

458 lines
10 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//=======================================================================
//
// 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;
}
}