You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
940 lines
29 KiB
940 lines
29 KiB
/*
|
|
** lzcomp.c - Routines used in Lempel-Ziv compression (a la 1977 article).
|
|
**
|
|
** Author: DavidDi
|
|
*/
|
|
|
|
|
|
|
|
// Headers
|
|
///////////
|
|
|
|
#include "pch.h"
|
|
#include "compress.h"
|
|
#include "resource.h"
|
|
|
|
#ifndef LZA_DLL
|
|
|
|
#include <dos.h>
|
|
#include <errno.h>
|
|
#include <io.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
** N.b., one reason DOS file handles are used for file references in this
|
|
** module is that using FILE *'s for file references poses a problem.
|
|
** fclose()'ing a file which was fopen()'ed in write "w" or append "a" mode
|
|
** stamps the file with the current date. This undoes the intended effect of
|
|
** CopyDateTimeStamp(). We could also get around this fclose() problem by
|
|
** first fclose()'ing the file, and then fopen()'ing it again in read "r"
|
|
** mode.
|
|
**
|
|
** Using file handles also allows us to bypass stream buffering, so reads and
|
|
** writes may be done with whatever buffer size we choose. Also, the
|
|
** lower-level DOS file handle functions are faster than their stream
|
|
** counterparts.
|
|
*/
|
|
|
|
|
|
INT Compress(
|
|
NOTIFYPROC pfnNotify,
|
|
LPWSTR pszSource,
|
|
LPWSTR pszDest,
|
|
BYTE byteAlgorithm,
|
|
BOOL bDoRename,
|
|
PLZINFO pLZI)
|
|
/*++
|
|
|
|
Compress one file to another.
|
|
|
|
Arguments: pszSource - name of file to compress
|
|
pszDest - name of compressed output file
|
|
byteAlgorithm - compression algorithm to use
|
|
byteExtensionChar - compressed file name extension character
|
|
|
|
Returns: int - TRUE if compression finished successfully. One of the
|
|
LZERROR_ codes if not.
|
|
|
|
Globals: none
|
|
*/
|
|
{
|
|
HANDLE doshSource = NULL; // input file handle
|
|
HANDLE doshDest = NULL; // output file handle
|
|
INT nRetVal = TRUE;
|
|
WCHAR szBuffer[MAX_PATH] = NULL_STRING;
|
|
WCHAR szDestFileName[MAX_PATH] = NULL_STRING;
|
|
WCHAR byteExtensionChar;
|
|
FH FHOut; // compressed header info struct
|
|
|
|
|
|
// Sanity check
|
|
if (!pLZI) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// Set up input file handle. Set cblInSize to length of input file.
|
|
if ((nRetVal = GetIOHandle(pszSource, READ_IT, &doshSource, &pLZI->cblInSize)) != TRUE)
|
|
{
|
|
DISPLAY_MESSAGE1( stderr, szBuffer, GetResString(IDS_FILE_NOT_FOUND), _X(pszSource) );
|
|
return(nRetVal);
|
|
}
|
|
|
|
// Rewind input file.
|
|
if( 0 != SetFilePointer(doshSource, 0L, NULL, FILE_BEGIN) )
|
|
{
|
|
CloseHandle(doshSource);
|
|
return(FALSE);
|
|
}
|
|
|
|
// Create destination file name.
|
|
|
|
STRCPY(szDestFileName, pszDest);
|
|
|
|
if (bDoRename == TRUE)
|
|
// Rename output file.
|
|
byteExtensionChar = MakeCompressedNameW(szDestFileName);
|
|
else
|
|
byteExtensionChar = '\0';
|
|
|
|
// Ask if we should compress this file.
|
|
if (! (*pfnNotify)(pszSource, szDestFileName, NOTIFY_START_COMPRESS))
|
|
{
|
|
// Don't compress file. This error condition should be handled in
|
|
// pfnNotify, so indicate that it is not necessary for the caller to
|
|
// display an error message.
|
|
CloseHandle( doshSource );
|
|
return(FALSE);
|
|
}
|
|
|
|
// Setup output file handle.
|
|
if ((nRetVal = GetIOHandle(szDestFileName, WRITE_IT, &doshDest, &pLZI->cblInSize)) != TRUE)
|
|
{
|
|
DISPLAY_MESSAGE1( stderr, szBuffer, GetResString(IDS_FILE_NOT_FOUND), pszSource );
|
|
CloseHandle(doshSource);
|
|
return(nRetVal);
|
|
}
|
|
|
|
// Fill in compressed file header.
|
|
MakeHeader(& FHOut, byteAlgorithm, byteExtensionChar, pLZI);
|
|
|
|
// Write compressed file header to output file.
|
|
if ((nRetVal = WriteHdr(& FHOut, doshDest, pLZI)) != TRUE)
|
|
{
|
|
CloseHandle( doshSource );
|
|
CloseHandle(doshDest);
|
|
DISPLAY_MESSAGE( stderr, GetResString( IDS_FAILED_WRITE_HDR ) );
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
|
|
// Compress input file into output file.
|
|
switch (byteAlgorithm)
|
|
{
|
|
case ALG_FIRST:
|
|
|
|
case ALG_LZ:
|
|
|
|
nRetVal = LZEncode(doshSource, doshDest, pLZI);
|
|
break;
|
|
|
|
default:
|
|
nRetVal = FALSE;
|
|
break;
|
|
}
|
|
|
|
if (nRetVal != TRUE)
|
|
{
|
|
CloseHandle(doshSource);
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
// Copy date and time stamp from source file to destination file.
|
|
nRetVal = CopyDateTimeStamp(doshSource, doshDest);
|
|
|
|
// Close files.
|
|
CloseHandle(doshSource);
|
|
CloseHandle(doshDest);
|
|
|
|
return(nRetVal);
|
|
}
|
|
|
|
INT LZEncode(HANDLE doshSource, HANDLE doshDest, PLZINFO pLZI)
|
|
/*
|
|
** int LZEncode(int doshSource, int doshDest);
|
|
**
|
|
** Compress input file into output file.
|
|
**
|
|
** Arguments: doshSource - open DOS file handle of input file
|
|
** doshDest - open DOS file handle of output file
|
|
**
|
|
** Returns: int - TRUE if compression was successful. One of the LZERROR_
|
|
** codes if the compression failed.
|
|
**
|
|
** Globals:
|
|
*/
|
|
{
|
|
INT i, len, f,
|
|
iCurChar, // current ring buffer position
|
|
iCurString, // start of current string in ring buffer
|
|
iCodeBuf, // index of next open buffer position
|
|
cbLastMatch; // length of last match
|
|
BYTE byte, // temporary storage for next byte to write
|
|
byteMask, // bit mask (and counter) for eight code units
|
|
codeBuf[1 + 8 * MAX_LITERAL_LEN]; // temporary storage for encoded data
|
|
|
|
#if 0
|
|
pLZI->cbMaxMatchLen = LZ_MAX_MATCH_LEN;
|
|
#else
|
|
pLZI->cbMaxMatchLen = FIRST_MAX_MATCH_LEN;
|
|
#endif
|
|
|
|
ResetBuffers();
|
|
|
|
pLZI->cblOutSize += HEADER_LEN;
|
|
|
|
// Initialize encoding trees.
|
|
if (!LZInitTree(pLZI)) {
|
|
return( LZERROR_GLOBALLOC );
|
|
}
|
|
|
|
// CodeBuf[1..16] saves eight units of code, and CodeBuf[0] works as eight
|
|
// flags. '1' representing that the unit is an unencoded letter (1 byte),
|
|
// '0' a position-and-length pair (2 bytes). Thus, eight units require at
|
|
// most 16 bytes of code, plus the one byte of flags.
|
|
codeBuf[0] = (BYTE)0;
|
|
byteMask = (BYTE)1;
|
|
iCodeBuf = 1;
|
|
|
|
iCurString = 0;
|
|
iCurChar = RING_BUF_LEN - pLZI->cbMaxMatchLen;
|
|
|
|
for (i = 0; i < RING_BUF_LEN - pLZI->cbMaxMatchLen; i++)
|
|
pLZI->rgbyteRingBuf[i] = BUF_CLEAR_BYTE;
|
|
|
|
// Read bytes into the last cbMaxMatchLen bytes of the buffer.
|
|
for (len = 0; len < pLZI->cbMaxMatchLen && ((f = ReadByte(byte)) != END_OF_INPUT);
|
|
len++)
|
|
{
|
|
if (f != TRUE) {
|
|
return( f );
|
|
}
|
|
|
|
pLZI->rgbyteRingBuf[iCurChar + len] = byte;
|
|
}
|
|
|
|
// Insert the cbMaxMatchLen strings, each of which begins with one or more
|
|
// 'space' characters. Note the order in which these strings are inserted.
|
|
// This way, degenerate trees will be less likely to occur.
|
|
for (i = 1; i <= pLZI->cbMaxMatchLen; i++)
|
|
LZInsertNode(iCurChar - i, FALSE, pLZI);
|
|
|
|
// Finally, insert the whole string just read. The global variables
|
|
// cbCurMatch and iCurMatch are set.
|
|
LZInsertNode(iCurChar, FALSE, pLZI);
|
|
|
|
do // while (len > 0)
|
|
{
|
|
// cbCurMatch may be spuriously long near the end of text.
|
|
if (pLZI->cbCurMatch > len)
|
|
pLZI->cbCurMatch = len;
|
|
|
|
if (pLZI->cbCurMatch <= MAX_LITERAL_LEN)
|
|
{
|
|
// This match isn't long enough to encode, so copy it directly.
|
|
pLZI->cbCurMatch = 1;
|
|
// Set 'one uncoded byte' bit flag.
|
|
codeBuf[0] |= byteMask;
|
|
// Write literal byte.
|
|
codeBuf[iCodeBuf++] = pLZI->rgbyteRingBuf[iCurChar];
|
|
}
|
|
else
|
|
{
|
|
// This match is long enough to encode. Send its position and
|
|
// length pair. N.b., pLZI->cbCurMatch > MAX_LITERAL_LEN.
|
|
codeBuf[iCodeBuf++] = (BYTE)pLZI->iCurMatch;
|
|
codeBuf[iCodeBuf++] = (BYTE)((pLZI->iCurMatch >> 4 & 0xf0) |
|
|
(pLZI->cbCurMatch - (MAX_LITERAL_LEN + 1)));
|
|
}
|
|
|
|
// Shift mask left one bit.
|
|
if ((byteMask <<= 1) == (BYTE)0)
|
|
{
|
|
// Send at most 8 units of code together.
|
|
for (i = 0; i < iCodeBuf; i++)
|
|
if ((f = WriteByte(codeBuf[i])) != TRUE) {
|
|
return( f );
|
|
}
|
|
|
|
// Reset flags and mask.
|
|
codeBuf[0] = (BYTE)0;
|
|
byteMask = (BYTE)1;
|
|
iCodeBuf = 1;
|
|
}
|
|
|
|
cbLastMatch = pLZI->cbCurMatch;
|
|
|
|
for (i = 0; i < cbLastMatch && ((f = ReadByte(byte)) != END_OF_INPUT);
|
|
i++)
|
|
{
|
|
if (f != TRUE) {
|
|
return( f );
|
|
}
|
|
|
|
// Delete old string.
|
|
LZDeleteNode(iCurString, pLZI);
|
|
pLZI->rgbyteRingBuf[iCurString] = byte;
|
|
|
|
// If the start position is near the end of buffer, extend the
|
|
// buffer to make string comparison easier.
|
|
if (iCurString < pLZI->cbMaxMatchLen - 1)
|
|
pLZI->rgbyteRingBuf[iCurString + RING_BUF_LEN] = byte;
|
|
|
|
// Increment position in ring buffer modulo RING_BUF_LEN.
|
|
iCurString = (iCurString + 1) & (RING_BUF_LEN - 1);
|
|
iCurChar = (iCurChar + 1) & (RING_BUF_LEN - 1);
|
|
|
|
// Register the string in rgbyteRingBuf[r..r + cbMaxMatchLen - 1].
|
|
LZInsertNode(iCurChar, FALSE, pLZI);
|
|
}
|
|
|
|
while (i++ < cbLastMatch)
|
|
{
|
|
// No need to read after the end of the input, but the buffer may
|
|
// not be empty.
|
|
LZDeleteNode(iCurString, pLZI);
|
|
iCurString = (iCurString + 1) & (RING_BUF_LEN - 1);
|
|
iCurChar = (iCurChar + 1) & (RING_BUF_LEN - 1);
|
|
if (--len)
|
|
LZInsertNode(iCurChar, FALSE, pLZI);
|
|
}
|
|
} while (len > 0); // until there is no input to process
|
|
|
|
if (iCodeBuf > 1)
|
|
// Send remaining code.
|
|
for (i = 0; i < iCodeBuf; i++)
|
|
if ((f = WriteByte(codeBuf[i])) != TRUE) {
|
|
return( f );
|
|
}
|
|
|
|
// Flush output buffer to file.
|
|
if ((f = FlushOutputBuffer(doshDest, pLZI)) != TRUE) {
|
|
return( f );
|
|
}
|
|
|
|
LZFreeTree(pLZI);
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
INT WriteHdr(PFH pFH, HANDLE doshDest, PLZINFO pLZI)
|
|
/*
|
|
** int WriteHdr(PFH pFH, int doshDest);
|
|
**
|
|
** Write compressed file header to output file.
|
|
**
|
|
** Arguments: pFH - pointer to source header information structure
|
|
** doshDest - DOS file handle of open output file
|
|
**
|
|
** Returns: int - TRUE if successful. LZERROR_BADOUTHANDLE if
|
|
** unsuccessful.
|
|
**
|
|
** Globals: none
|
|
**
|
|
** header format:
|
|
** 8 bytes --> compressed file signature
|
|
** 1 byte --> algorithm label
|
|
** 1 byte --> extension char
|
|
** 4 bytes --> uncompressed file size (LSB to MSB)
|
|
**
|
|
** length = 14 bytes
|
|
*/
|
|
{
|
|
INT i, j;
|
|
DWORD ucbWritten;
|
|
BYTE rgbyteHeaderBuf[HEADER_LEN]; // temporary storage for next header byte to write
|
|
LPWSTR lpBuf = NULL;
|
|
|
|
// Sanity check
|
|
if (!pLZI) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// Copy the compressed file signature.
|
|
for (i = 0; i < COMP_SIG_LEN; i++)
|
|
rgbyteHeaderBuf[i] = pFH->rgbyteMagic[i];
|
|
|
|
// Copy the algorithm label and file name extension character.
|
|
rgbyteHeaderBuf[i++] = pFH->byteAlgorithm;
|
|
rgbyteHeaderBuf[i++] = (BYTE) pFH->byteExtensionChar;
|
|
rgbyteHeaderBuf[i++] = (BYTE) pFH->byteExtensionChar+1;
|
|
|
|
// Copy input file size (long ==> 4 bytes),
|
|
// LSB first to MSB last.
|
|
for (j = 0; j < 4; j++)
|
|
rgbyteHeaderBuf[i++] = (BYTE)((pFH->cbulUncompSize >> (8 * j)) &
|
|
(DWORD)BYTE_MASK);
|
|
|
|
// Write header to file.
|
|
if ( FALSE == WriteFile(doshDest,
|
|
rgbyteHeaderBuf,
|
|
HEADER_LEN,
|
|
&ucbWritten,
|
|
NULL))
|
|
{
|
|
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
GetLastError(),
|
|
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
|
|
(LPWSTR) &lpBuf,
|
|
0,
|
|
NULL );
|
|
|
|
DISPLAY_MESSAGE( stdout, lpBuf );
|
|
LocalFree( lpBuf );
|
|
|
|
#ifdef LZA_DLL
|
|
if (ucbWritten == (DWORD)(-1))
|
|
#else
|
|
if (_error != 0U)
|
|
#endif
|
|
// Bad DOS file handle.
|
|
return(FALSE);
|
|
else
|
|
// Insufficient space on destination drive.
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
// Keep track of bytes written.
|
|
pLZI->cblOutSize += (LONG)ucbWritten;
|
|
|
|
// Header written ok.
|
|
return(TRUE);
|
|
}
|
|
|
|
INT WriteOutBuf(BYTE byteNext, HANDLE doshDest, PLZINFO pLZI)
|
|
/*++
|
|
Dumps output buffer to output file. Prompts for new floppy disk if the
|
|
old one if full. Continues dumping to output file of same name on new
|
|
floppy disk.
|
|
|
|
Arguments: byteNext - first byte to be added to empty buffer after buffer
|
|
is written
|
|
doshDest - output DOS file handle
|
|
|
|
Returns: int - TRUE if successful. LZERROR_BADOUTHANDLE or
|
|
LZERROR_WRITE if unsuccessful.
|
|
|
|
Globals: pbyteOutBuf - reset to point to free byte after byteNext in
|
|
rgbyteOutBuf
|
|
--*/
|
|
|
|
{
|
|
DWORD ucbToWrite = 0; // number of bytes to write from buffer
|
|
DWORD ucbWritten = 0; // number of bytes actually written
|
|
DWORD ucbTotWritten = 0; // total number of bytes written to output
|
|
BOOL bStatus = FALSE;
|
|
|
|
// !!! Assumes pLZI parm is valid. No sanity check (should be done above in caller).
|
|
|
|
// How much of the buffer should be written to the output file?
|
|
ucbTotWritten = ucbToWrite = (DWORD)(pLZI->pbyteOutBuf - pLZI->rgbyteOutBuf);
|
|
// Reset pointer to beginning of buffer.
|
|
pLZI->pbyteOutBuf = pLZI->rgbyteOutBuf;
|
|
|
|
// Write to ouput file.
|
|
|
|
bStatus = WriteFile(doshDest, pLZI->pbyteOutBuf, ucbToWrite, &ucbWritten, NULL);
|
|
if ( ucbWritten != ucbToWrite )
|
|
{
|
|
#ifdef LZA_DLL
|
|
if (ucbWritten == (DWORD)(-1)) {
|
|
#else
|
|
if (_error != 0U) {
|
|
#endif
|
|
// Bad DOS file handle.
|
|
return(FALSE);
|
|
}
|
|
else {
|
|
// Insufficient space on destination drive.
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
// Add the next byte to the buffer.
|
|
*pLZI->pbyteOutBuf++ = byteNext;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
INT ReadInBuf(BYTE ARG_PTR *pbyte, HANDLE doshSource, PLZINFO pLZI)
|
|
/*++
|
|
int ReadInBuf(BYTE ARG_PTR *pbyte, int doshSource);
|
|
|
|
Read input file into input buffer.
|
|
|
|
Arguments: pbyte - pointer to storage for first byte read from file
|
|
into buffer
|
|
doshSource - DOS file handle to open input file
|
|
|
|
Returns: int - TRUE or END_OF_INPUT if successful. LZERROR_BADINHANDLE
|
|
if not.
|
|
|
|
Globals: rgbyteInBuf[0] - holds last byte from previous buffer
|
|
pbyteInBufEnd - set to point to first byte beyond end of data
|
|
in input buffer
|
|
bLastUsed - reset to FALSE if currently TRUE
|
|
--*/
|
|
|
|
{
|
|
DWORD ucbRead = 0; // number of bytes actually read
|
|
DWORD dwBytesRead = 0;
|
|
|
|
// !!! Assumes pLZI parm is valid. No sanity check (should be done above in caller).
|
|
|
|
pLZI->rgbyteInBuf[0] = *(pLZI->pbyteInBufEnd - 1);
|
|
|
|
ReadFile(doshSource, &pLZI->rgbyteInBuf[1], pLZI->ucbInBufLen, &ucbRead, NULL);
|
|
if (ucbRead != pLZI->ucbInBufLen)
|
|
{
|
|
#ifdef LZA_DLL
|
|
if (ucbRead == (DWORD)(-1)) {
|
|
#else
|
|
if (_error != 0U) {
|
|
#endif
|
|
// We were handed a bad input file handle.
|
|
return(FALSE);
|
|
}
|
|
else if (ucbRead > 0U)
|
|
// Read last ucbRead bytes of input file. Change input buffer end
|
|
// to account for shorter read.
|
|
pLZI->pbyteInBufEnd = &pLZI->rgbyteInBuf[1] + ucbRead;
|
|
else { // (ucbRead == 0U) {
|
|
// We couldn't read any bytes from input file (EOF reached).
|
|
return(END_OF_INPUT);
|
|
}
|
|
}
|
|
|
|
// Reset read pointer to beginning of input buffer.
|
|
pLZI->pbyteInBuf = &pLZI->rgbyteInBuf[1];
|
|
|
|
// Was an UnreadByte() done at the beginning of the last buffer?
|
|
if (pLZI->bLastUsed)
|
|
{
|
|
// Return the last byte from the previous input buffer
|
|
*pbyte = pLZI->rgbyteInBuf[0];
|
|
pLZI->bLastUsed = FALSE;
|
|
}
|
|
else
|
|
// Return the first byte from the new input buffer.
|
|
*pbyte = *pLZI->pbyteInBuf++;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
VOID MakeHeader(PFH pFHBlank, BYTE byteAlgorithm,
|
|
WCHAR byteExtensionChar, PLZINFO pLZI)
|
|
/*++
|
|
void MakeHeader(PFH pFHBlank, BYTE byteAlgorithm,
|
|
BYTE byteExtensionChar);
|
|
|
|
Arguments: pFHBlank - pointer to compressed file header struct
|
|
that is to be filled in
|
|
byteAlgorithm - algorithm label
|
|
byteExtensionChar - uncompressed file name extension character
|
|
|
|
Returns: void
|
|
|
|
Globals: none
|
|
|
|
Global cblInSize is used to fill in expanded file length field.
|
|
Compressed file length field is set to 0 since it isn't written.
|
|
|
|
--*/
|
|
{
|
|
INT i;
|
|
|
|
// !!! Assumes pLZI parm is valid. No sanity check (should be done above in caller).
|
|
|
|
// Fill in compressed file signature.
|
|
for (i = 0; i < COMP_SIG_LEN; i++)
|
|
pFHBlank->rgbyteMagic[i] = (BYTE)(*(COMP_SIG + i));
|
|
|
|
// Fill in algorithm and extesion character.
|
|
pFHBlank->byteAlgorithm = byteAlgorithm;
|
|
pFHBlank->byteExtensionChar = byteExtensionChar;
|
|
|
|
// Fill in file sizes. (cbulCompSize not written to compressed file
|
|
// header, so just set it to 0UL.)
|
|
pFHBlank->cbulUncompSize = (DWORD)pLZI->cblInSize;
|
|
pFHBlank->cbulCompSize = 0UL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
FileTimeIsNewer( LPWSTR pszFile1,
|
|
LPWSTR pszFile2 )
|
|
|
|
/*++ static BOOL FileTimeIsNewer( const char* pszFile1, const char* pszFile2 );
|
|
|
|
Return value is TRUE if time stamp on pszFile1 is newer than the
|
|
time stamp on pszFile2. If either of the two files do not exist,
|
|
the return value is also TRUE (for indicating that pszFile2 should
|
|
be update from pszFile1). Otherwise, the return value is FALSE.
|
|
--*/
|
|
|
|
{
|
|
|
|
struct _stat StatBufSource,
|
|
StatBufDest;
|
|
|
|
if (( _wstat( pszFile2, &StatBufDest )) ||
|
|
( _wstat( pszFile1, &StatBufSource )) ||
|
|
( StatBufSource.st_mtime > StatBufDest.st_mtime ))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
PLZINFO InitGlobalBuffers(
|
|
DWORD dwOutBufSize,
|
|
DWORD dwRingBufSize,
|
|
DWORD dwInBufSize)
|
|
{
|
|
PLZINFO pLZI;
|
|
|
|
if (!(pLZI = (PLZINFO)LocalAlloc(LPTR, sizeof(LZINFO)))) {
|
|
return(NULL);
|
|
}
|
|
|
|
// Set up ring buffer. N.b., extra (cbStrMax - 1) bytes used to
|
|
// facilitate string comparisons near end of ring buffer.
|
|
// (The size allocated for the ring buffer may be at most 4224, since
|
|
// that's the ring buffer length embedded in the LZFile structs in
|
|
// lzexpand.h.)
|
|
|
|
if (dwRingBufSize == 0) {
|
|
dwRingBufSize = MAX_RING_BUF_LEN;
|
|
}
|
|
|
|
if ((pLZI->rgbyteRingBuf = (BYTE FAR *)FALLOC(dwRingBufSize * sizeof(BYTE))) == NULL)
|
|
// Bail out, since without the ring buffer, we can't decode anything.
|
|
return(NULL);
|
|
|
|
|
|
if (dwInBufSize == 0) {
|
|
dwInBufSize = MAX_IN_BUF_SIZE;
|
|
}
|
|
|
|
if (dwOutBufSize == 0) {
|
|
dwOutBufSize = MAX_OUT_BUF_SIZE;
|
|
}
|
|
|
|
for (pLZI->ucbInBufLen = dwInBufSize, pLZI->ucbOutBufLen = dwOutBufSize;
|
|
pLZI->ucbInBufLen > 0U && pLZI->ucbOutBufLen > 0U;
|
|
pLZI->ucbInBufLen -= IN_BUF_STEP, pLZI->ucbOutBufLen -= OUT_BUF_STEP)
|
|
{
|
|
// Try to set up input buffer. N.b., extra byte because rgbyteInBuf[0]
|
|
// will be used to hold last byte from previous input buffer.
|
|
if ((pLZI->rgbyteInBuf = (BYTE *)FALLOC(pLZI->ucbInBufLen + 1U)) == NULL)
|
|
continue;
|
|
|
|
// And try to set up output buffer...
|
|
if ((pLZI->rgbyteOutBuf = (BYTE *)FALLOC(pLZI->ucbOutBufLen)) == NULL)
|
|
{
|
|
FFREE(pLZI->rgbyteInBuf);
|
|
continue;
|
|
}
|
|
|
|
return(pLZI);
|
|
}
|
|
|
|
// Insufficient memory for I/O buffers.
|
|
FFREE(pLZI->rgbyteRingBuf);
|
|
return(NULL);
|
|
}
|
|
|
|
PLZINFO InitGlobalBuffersEx()
|
|
{
|
|
return(InitGlobalBuffers(MAX_OUT_BUF_SIZE, MAX_RING_BUF_LEN, MAX_IN_BUF_SIZE));
|
|
}
|
|
|
|
VOID FreeGlobalBuffers(
|
|
PLZINFO pLZI)
|
|
{
|
|
|
|
// Sanity check
|
|
|
|
if (!pLZI) {
|
|
return;
|
|
}
|
|
|
|
if (pLZI->rgbyteRingBuf)
|
|
{
|
|
FFREE(pLZI->rgbyteRingBuf);
|
|
pLZI->rgbyteRingBuf = NULL;
|
|
}
|
|
|
|
if (pLZI->rgbyteInBuf)
|
|
{
|
|
FFREE(pLZI->rgbyteInBuf);
|
|
pLZI->rgbyteInBuf = NULL;
|
|
}
|
|
|
|
if (pLZI->rgbyteOutBuf)
|
|
{
|
|
FFREE(pLZI->rgbyteOutBuf);
|
|
pLZI->rgbyteOutBuf = NULL;
|
|
}
|
|
|
|
// Buffers deallocated ok.
|
|
|
|
// reset thread info
|
|
LocalFree(pLZI);
|
|
}
|
|
|
|
|
|
INT
|
|
GetIOHandle(LPWSTR pszFileName,
|
|
BOOL bRead,
|
|
HANDLE *pdosh,
|
|
LONG *pcblInSize)
|
|
/*
|
|
** int GetIOHandle(char ARG_PTR *pszFileName, BOOL bRead, int ARG_PTR *pdosh);
|
|
**
|
|
** Opens input and output files.
|
|
**
|
|
** Arguments: pszFileName - source file name
|
|
** bRead - mode for opening file TRUE for read and FALSE
|
|
** for write
|
|
** pdosh - pointer to buffer for DOS file handle to be
|
|
** filled in
|
|
**
|
|
** Returns: int - TRUE if file opened successfully. LZERROR_BADINHANDLE
|
|
** if input file could not be opened. LZERROR_BADOUTHANDLE
|
|
** if output file could not be opened. Fills in
|
|
** *pdosh with open DOS file handle, or NO_DOSH if
|
|
** pszFileName is NULL.
|
|
**
|
|
** Globals: cblInSize - set to length of input file
|
|
*/
|
|
{
|
|
LPVOID lpBuf = NULL;
|
|
|
|
if (pszFileName == NULL)
|
|
*pdosh = NULL;
|
|
else if (bRead == WRITE_IT)
|
|
{
|
|
// Set up output DOS file handle.
|
|
if ((*pdosh = CreateFile( pszFileName, GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL)) == INVALID_HANDLE_VALUE )
|
|
return(FALSE);
|
|
|
|
}
|
|
else // (bRead == READ_IT)
|
|
{
|
|
if ((*pdosh = CreateFile( pszFileName, GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL)) == INVALID_HANDLE_VALUE)
|
|
return(FALSE);
|
|
|
|
// Move to the end of the input file to find its length,
|
|
// then return to the beginning.
|
|
if ((*pcblInSize = GetFileSize( *pdosh, NULL )) == -1 )
|
|
{
|
|
CloseHandle(*pdosh);
|
|
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
GetLastError(),
|
|
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
|
|
(LPWSTR) &lpBuf,
|
|
0,
|
|
NULL );
|
|
|
|
DISPLAY_MESSAGE( stdout, (LPWSTR) lpBuf );
|
|
//release memory allocated by LocalAlloc
|
|
LocalFree( lpBuf );
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
ProcessNotification(LPWSTR pszSource,
|
|
LPWSTR pszDest,
|
|
WORD wNotification
|
|
)
|
|
|
|
/*
|
|
static BOOL ProcessNotification(char ARG_PTR *pszSource,
|
|
char ARG_PTR *pszDest,
|
|
WORD wNotification);
|
|
Callback function during file processing.
|
|
|
|
Arguments: pszSource - source file name
|
|
pszDest - destination file name
|
|
wNotification - process type query
|
|
|
|
Returns: BOOL - (wNotification == NOTIFY_START_*):
|
|
TRUE if the source file should be "processed" into
|
|
the destination file. FALSE if not.
|
|
else
|
|
TRUE.
|
|
|
|
Globals: none
|
|
--*/
|
|
{
|
|
WCHAR* szBuffer = NULL;
|
|
DWORD dwSize = 0;
|
|
|
|
switch(wNotification)
|
|
{
|
|
case NOTIFY_START_COMPRESS:
|
|
{
|
|
// Fail if the source and destination files are identical.
|
|
if( lstrcmp( pszSource, pszDest ) == 0 )
|
|
{
|
|
dwSize = lstrlen( GetResString( IDS_COLLISION ) )+ lstrlen(pszSource) + 10;
|
|
szBuffer = malloc( dwSize*sizeof(WCHAR) );
|
|
if( NULL == szBuffer )
|
|
{
|
|
DISPLAY_MESSAGE( stderr, GetResString(IDS_TAG_ERROR) );
|
|
DISPLAY_MESSAGE( stderr, EMPTY_SPACE );
|
|
SetLastError( ERROR_OUTOFMEMORY );
|
|
SaveLastError();
|
|
DISPLAY_MESSAGE( stderr, GetReason() );
|
|
return( EXIT_FAILURE );
|
|
}
|
|
DISPLAY_MESSAGE1( stderr, szBuffer, GetResString( IDS_COLLISION ), pszSource );
|
|
free( szBuffer );
|
|
return FALSE;
|
|
}
|
|
|
|
// Display start message.
|
|
switch (byteAlgorithm)
|
|
{
|
|
case LZX_ALG:
|
|
dwSize = lstrlen( GetResString( IDS_COMPRESSING_LZX ) )+ lstrlen(pszSource) + lstrlen(pszDest)+10;
|
|
szBuffer = malloc( dwSize*sizeof(WCHAR) );
|
|
if( NULL == szBuffer )
|
|
{
|
|
DISPLAY_MESSAGE( stderr, GetResString(IDS_TAG_ERROR) );
|
|
DISPLAY_MESSAGE( stderr, EMPTY_SPACE );
|
|
SetLastError( ERROR_OUTOFMEMORY );
|
|
SaveLastError();
|
|
DISPLAY_MESSAGE( stderr, GetReason() );
|
|
return( EXIT_FAILURE );
|
|
}
|
|
swprintf( szBuffer, GetResString( IDS_COMPRESSING_LZX ), pszSource, pszDest,
|
|
CompressionMemoryFromTCOMP(DiamondCompressionType) );
|
|
ShowMessage(stdout, _X(szBuffer) );
|
|
free( szBuffer );
|
|
break;
|
|
|
|
case QUANTUM_ALG:
|
|
dwSize = lstrlen( GetResString( IDS_COMPRESSING_QUANTUM ) )+ lstrlen(pszSource) + lstrlen(pszDest)+10;
|
|
szBuffer = malloc( dwSize*sizeof(WCHAR) );
|
|
if( NULL == szBuffer )
|
|
{
|
|
DISPLAY_MESSAGE( stderr, GetResString(IDS_TAG_ERROR) );
|
|
DISPLAY_MESSAGE( stderr, EMPTY_SPACE );
|
|
SetLastError( ERROR_OUTOFMEMORY );
|
|
SaveLastError();
|
|
DISPLAY_MESSAGE( stderr, GetReason() );
|
|
return( EXIT_FAILURE );
|
|
}
|
|
swprintf( szBuffer, GetResString( IDS_COMPRESSING_QUANTUM ), pszSource, pszDest,
|
|
CompressionLevelFromTCOMP(DiamondCompressionType),
|
|
CompressionMemoryFromTCOMP(DiamondCompressionType)
|
|
);
|
|
ShowMessage( stdout, _X(szBuffer) );
|
|
free( szBuffer );
|
|
break;
|
|
|
|
default:
|
|
dwSize = lstrlen( GetResString( IDS_COMPRESSING_MSZIP ) )+ lstrlen(pszSource) + lstrlen(pszDest)+10;
|
|
szBuffer = malloc( dwSize*sizeof(WCHAR) );
|
|
if( NULL == szBuffer )
|
|
{
|
|
DISPLAY_MESSAGE( stderr, GetResString(IDS_TAG_ERROR) );
|
|
DISPLAY_MESSAGE( stderr, EMPTY_SPACE );
|
|
SetLastError( ERROR_OUTOFMEMORY );
|
|
SaveLastError();
|
|
DISPLAY_MESSAGE( stderr, GetReason() );
|
|
return( EXIT_FAILURE );
|
|
}
|
|
swprintf(szBuffer,
|
|
(byteAlgorithm == MSZIP_ALG) ? GetResString( IDS_COMPRESSING_MSZIP ) : GetResString( IDS_COMPRESSING ),
|
|
pszSource,
|
|
pszDest);
|
|
ShowMessage( stdout, _X(szBuffer) );
|
|
free( szBuffer );
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
INT CopyDateTimeStamp(HANDLE doshFrom, HANDLE doshTo)
|
|
/*++
|
|
|
|
|
|
Copy date and time stamp from one file to another.
|
|
|
|
Arguments: doshFrom - date and time stamp source DOS file handle
|
|
doshTo - target DOS file handle
|
|
|
|
Returns: TRUE if successful. LZERROR_BADINHANDLE or
|
|
LZERROR_BADOUTHANDLE if unsuccessful.
|
|
|
|
Globals: none
|
|
|
|
N.b., stream-style I/O routines like fopen() and fclose() may counter the
|
|
intended effect of this function. fclose() writes the current date to any
|
|
file it's called with which was opened in write "w" or append "a" mode.
|
|
One way to get around this in order to modify the date of a file opened
|
|
for writing or appending by fopen() is to fclose() the file and fopen() it
|
|
again in read "r" mode. Then set its date and time stamp with
|
|
CopyDateTimeStamp().
|
|
--*/
|
|
|
|
{
|
|
|
|
FILETIME lpCreationTime, lpLastAccessTime, lpLastWriteTime;
|
|
|
|
if(!GetFileTime((HANDLE) doshFrom, &lpCreationTime, &lpLastAccessTime,
|
|
&lpLastWriteTime)){
|
|
return(FALSE);
|
|
}
|
|
if(!SetFileTime((HANDLE) doshTo, &lpCreationTime, &lpLastAccessTime,
|
|
&lpLastWriteTime)){
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|