982 lines
30 KiB
C
982 lines
30 KiB
C
/*
|
|
** winlzexp.c - Windows LZExpand library routines for manipulating compressed
|
|
** files.
|
|
**
|
|
** Author: DavidDi
|
|
*/
|
|
|
|
|
|
/*
|
|
** Notes:
|
|
** -----
|
|
**
|
|
** The LZInit() function returns either DOS handles or LZFile struct
|
|
** identifiers of some kind, depending upon how it is called. The LZ
|
|
** functions LZSeek(), LZRead(), and LZClose() needed some way to
|
|
** differentiate between DOS file handles and the LZFile struct identifiers.
|
|
** As the functions stand now, they use DOS file handles as themselves, and
|
|
** table offsets as LZFile identifiers. The table offsets are incremented by
|
|
** some base value, LZ_TABLE_BIAS, in order to push their values past all
|
|
** possible DOS file handle values. The table offsets (- LZ_TABLE_BIAS) are
|
|
** used as indices in rghLZFileTable[] to retrieve a global handle to an
|
|
** LZFile struct. The table of global handles is allocated statically from
|
|
** the DLL's data segment. The LZFile struct's are allocated from global
|
|
** heap space and are moveable. This scheme might also be implemented as a
|
|
** linked list of global handles.
|
|
**
|
|
** Now the resulting benefit from this scheme is that DOS file handles and
|
|
** LZFile struct identifiers can be differentiated, since DOS file handles
|
|
** are always < LZ_TABLE_BIAS, and LZFile struct identifiers are always
|
|
** >= LZ_TABLE_BIAS. This dichotomy may be used in macros, like the sample
|
|
** ones provided in lzexpand.h, to select the appropriate function to call
|
|
** (e.g., LZSeek() or _llseek()) in order to avoid the overhead of an extra
|
|
** function call for uncompressed files. LZSeek(), LZRead(), and LZClose()
|
|
** are, however, "smart" enough to figure out whether they are dealing with
|
|
** DOS file handles or table offsets, and take action appropriately. As an
|
|
** extreme example, LZOpenFile(), LZSeek(), LZRead, and LZClose() can be used
|
|
** as replacements for OpenFile(), _llseek(), _lread(), and _lclose. In this
|
|
** case, the program using the DLL functions could call them without ever
|
|
** caring whether the files it was reading were LZ compressed or not.
|
|
*/
|
|
|
|
|
|
/* WIN32 MODS
|
|
|
|
** Since the above is a DOS only hack, I have to change the logic for
|
|
** for the 0-255 file handle deal'o. The original code, tests greater than
|
|
** LZ_TABLE_BIAS for file structures. What I will do, is convert file handles
|
|
** returned from OpenFile, to a range 0-255. Once the test is complete, I'll
|
|
** use the file handle as an offset into a 255 element array, which will
|
|
** contain the WIN32 file handle. So there will be an additional array
|
|
** fhWin32File[255], which will be allocated sequencially starting at 0.
|
|
** Unfortunately, this means everywhere the file handle is used, must be converted
|
|
*/
|
|
|
|
// Headers
|
|
///////////
|
|
#include <basedll.h>
|
|
#define LZA_DLL
|
|
#include "lz_common.h"
|
|
#include "lz_buffers.h"
|
|
#include "lz_header.h"
|
|
#include "lzcommon.h"
|
|
|
|
#include "lzpriv.h"
|
|
#include "wchar.h"
|
|
|
|
#if DEBUG
|
|
#include "stdio.h"
|
|
#endif
|
|
|
|
// Globals
|
|
///////////
|
|
|
|
// Semaphore for File Table allocation
|
|
RTL_CRITICAL_SECTION semTable;
|
|
BOOL fCSInit = FALSE;
|
|
|
|
// table of handles to LZFile structs
|
|
HANDLE rghLZFileTable[MAX_LZFILES] = {0};
|
|
|
|
// next free entry in rghLZFileTable[]
|
|
static INT iNextLZFileEntry = 0;
|
|
|
|
HFILE fhWin32File[MAX_LZFILES] = {0};
|
|
|
|
/*
|
|
** int APIENTRY LZInit(int hWin32File);
|
|
**
|
|
** Sets up LZFile struct for a file that has already been opened by
|
|
** OpenFile().
|
|
**
|
|
** Arguments: hWin32File - source DOS file handle
|
|
**
|
|
** Returns: int - LZFile struct table offset or DOS file handle if
|
|
** successful. One of the LZERROR_ codes if unsuccessful.
|
|
**
|
|
** Globals: iNextLZFile entry advanced, or returned to beginning from end.
|
|
*/
|
|
INT APIENTRY LZInit(INT hWin32File)
|
|
{
|
|
HANDLE hLZFile; // handle to new LZFile struct
|
|
LZFile *lpLZ; // pointer to new LZFile struct
|
|
FH FHComp; // header info structure from input file
|
|
BOOL bHdr; // holds GetHdr() return value
|
|
INT iTableIndex, // holds rghLZFileTable[] slot to be filled by
|
|
// new LZFile struct handle
|
|
iStartEntry; // original value of iNextLZFileEntry
|
|
|
|
LONG cblInSize = 0;
|
|
INT nRet;
|
|
|
|
if (!fCSInit) {
|
|
if (!NT_SUCCESS(RtlInitializeCriticalSection(&semTable))) {
|
|
return LZERROR_GLOBALLOC;
|
|
}
|
|
fCSInit = TRUE;
|
|
}
|
|
|
|
// Did the read succeed?
|
|
if ((bHdr = GetHdr((FH *)&FHComp, hWin32File, &cblInSize)) != TRUE
|
|
&& cblInSize >= (LONG)HEADER_LEN) {
|
|
|
|
return(LZERROR_BADINHANDLE);
|
|
}
|
|
|
|
// Check for uncompressed input file.
|
|
if (bHdr != TRUE || IsCompressed(& FHComp) != TRUE)
|
|
{
|
|
// This is an uncompressed file - rewind it.
|
|
if (FSEEK(hWin32File, 0L, SEEK_SET) != 0L) {
|
|
return(LZERROR_BADINHANDLE);
|
|
}
|
|
else {
|
|
// And return DOS file handle.
|
|
return(ConvertWin32FHToDos(hWin32File));
|
|
}
|
|
}
|
|
|
|
// Check compression algorithm used.
|
|
if (RecognizeCompAlg(FHComp.byteAlgorithm) != TRUE) {
|
|
return(LZERROR_UNKNOWNALG);
|
|
}
|
|
|
|
// Find next free rghLZFileTable[] entry. N.b., we depend upon LZClose()
|
|
// to free unused entries up.
|
|
RtlEnterCriticalSection(&semTable);
|
|
|
|
iStartEntry = iNextLZFileEntry;
|
|
|
|
while (rghLZFileTable[iNextLZFileEntry] != NULL)
|
|
{
|
|
if (++iNextLZFileEntry >= MAX_LZFILES)
|
|
// Return to beginning of table.
|
|
iNextLZFileEntry = 0;
|
|
|
|
if (iNextLZFileEntry == iStartEntry) {
|
|
// We've gone full circle through rghLZFileTable[].
|
|
// It's full, so bail out.
|
|
nRet = LZERROR_GLOBALLOC;
|
|
goto LZInitExit;
|
|
}
|
|
}
|
|
|
|
// Keep track of the rghLZFileTable() slot to be filled by a handle to the
|
|
// new LZFile struct.
|
|
iTableIndex = iNextLZFileEntry;
|
|
|
|
// Allocate global storage for the new LZFile struct, initializing all
|
|
// fields to 0.
|
|
|
|
hLZFile = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (DWORD)sizeof(LZFile));
|
|
if (!hLZFile) {
|
|
|
|
nRet = LZERROR_GLOBALLOC;
|
|
goto LZInitExit;
|
|
}
|
|
|
|
// Lock that baby up.
|
|
if ((lpLZ = (LZFile *)GlobalLock(hLZFile)) == NULL)
|
|
{
|
|
GlobalFree(hLZFile);
|
|
|
|
nRet =LZERROR_GLOBLOCK;
|
|
goto LZInitExit;
|
|
}
|
|
|
|
// Initialize the new LZFile struct's general information fields.
|
|
lpLZ->dosh = hWin32File;
|
|
lpLZ->byteAlgorithm = FHComp.byteAlgorithm;
|
|
lpLZ->wFlags = 0;
|
|
lpLZ->cbulUncompSize = FHComp.cbulUncompSize;
|
|
lpLZ->cbulCompSize = FHComp.cbulCompSize;
|
|
lpLZ->lCurSeekPos = 0L;
|
|
|
|
// LZRead/LZSeeks expansion data is kept on a per file basis
|
|
lpLZ->pLZI = NULL;
|
|
|
|
// Enter new handle in table of handles.
|
|
rghLZFileTable[iTableIndex] = hLZFile;
|
|
|
|
/* WIN32 NOTE, dont need convert below, as forces a non file handle
|
|
* to the API.
|
|
*/
|
|
|
|
GlobalUnlock(hLZFile);
|
|
|
|
// Advance to next free entry.
|
|
if (++iNextLZFileEntry >= MAX_LZFILES)
|
|
iNextLZFileEntry = 0;
|
|
|
|
nRet = LZ_TABLE_BIAS + iTableIndex;
|
|
|
|
LZInitExit:
|
|
|
|
RtlLeaveCriticalSection(&semTable);
|
|
|
|
// Return rghLZFileTable[] offset of the new LZFile struct's handle's
|
|
// entry + the table bias.
|
|
return(nRet);
|
|
}
|
|
|
|
|
|
/*
|
|
** int APIENTRY GetExpandedNameA(LPSTR lpszSource, LPSTR lpszBuffer);
|
|
**
|
|
** Looks in the header of a compressed file to find its original expanded
|
|
** name.
|
|
**
|
|
** Arguments: lpszSource - name of input file
|
|
** lpszBuffer - pointer to a buffer that will be filled in with
|
|
** the expanded name of the compressed source file
|
|
**
|
|
** Returns: int - TRUE if successful. One of the LZERROR_ codes if
|
|
** unsuccessful.
|
|
**
|
|
** Globals: none
|
|
*/
|
|
INT APIENTRY GetExpandedNameA(LPSTR lpszSource, LPSTR lpszBuffer)
|
|
{
|
|
INT doshSource, // source DOS file handle
|
|
bHdr; // holds GetHdr() return value
|
|
FH FHComp; // header info structure from input file
|
|
OFSTRUCT ofOpenBuf; // source struct for OpenFile() call
|
|
LONG cblInSize = 0;
|
|
|
|
// Try to open the source file.
|
|
if ((doshSource = (HFILE)MOpenFile(lpszSource, (LPOFSTRUCT)(& ofOpenBuf), OF_READ))
|
|
== -1)
|
|
return(LZERROR_BADVALUE);
|
|
|
|
// Get the compressed file header.
|
|
if ((bHdr = GetHdr((FH *)&FHComp, doshSource, &cblInSize)) != TRUE
|
|
&& cblInSize >= (LONG)HEADER_LEN)
|
|
{
|
|
FCLOSE(doshSource);
|
|
return(LZERROR_BADVALUE);
|
|
}
|
|
|
|
// Close source file.
|
|
FCLOSE(doshSource);
|
|
|
|
// Return expanded name same as source name for uncompressed files.
|
|
STRCPY(lpszBuffer, lpszSource);
|
|
|
|
// Check for compressed input file.
|
|
if (bHdr == TRUE && IsCompressed(& FHComp) == TRUE)
|
|
MakeExpandedName(lpszBuffer, FHComp.byteExtensionChar);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/*
|
|
** int APIENTRY GetExpandedNameW(LPSTR lpszSource, LPSTR lpszBuffer);
|
|
**
|
|
** Wide Character version of GetExpandedName. Converts the filename to
|
|
** the ANSI Character set and calls the ANSI version.
|
|
**
|
|
*/
|
|
INT APIENTRY GetExpandedNameW(LPWSTR lpszSource, LPWSTR lpszBuffer)
|
|
{
|
|
UNICODE_STRING TempW;
|
|
ANSI_STRING TempA;
|
|
NTSTATUS Status;
|
|
NTSTATUS StatusR;
|
|
CHAR szBuffer[MAX_PATH];
|
|
|
|
|
|
TempW.Buffer = lpszSource;
|
|
TempW.Length = wcslen(lpszSource)*sizeof(WCHAR);
|
|
TempW.MaximumLength = TempW.Length + sizeof(WCHAR);
|
|
|
|
TempA.Buffer = szBuffer;
|
|
TempA.MaximumLength = MAX_PATH;
|
|
StatusR = RtlUnicodeStringToAnsiString(&TempA, &TempW, FALSE);
|
|
if (!NT_SUCCESS(StatusR))
|
|
return LZERROR_GLOBALLOC;
|
|
|
|
Status = GetExpandedNameA(szBuffer, (LPSTR)lpszBuffer);
|
|
|
|
if (Status != -1) {
|
|
strcpy(szBuffer, (LPSTR)lpszBuffer);
|
|
TempA.Length = (USHORT) strlen(szBuffer);
|
|
TempA.MaximumLength = TempA.Length+sizeof(CHAR);
|
|
|
|
TempW.Buffer = lpszBuffer;
|
|
TempW.MaximumLength = MAX_PATH;
|
|
StatusR = RtlAnsiStringToUnicodeString(&TempW, &TempA, FALSE);
|
|
if (!NT_SUCCESS(StatusR))
|
|
return LZERROR_GLOBALLOC;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// INT LZCreateFileW(LPCWSTR lpFileName, DWORD fdwAccess)
|
|
//
|
|
// Opens a file (using CreateFile) and sets up an LZFile struct for
|
|
// expanding it.
|
|
//
|
|
// Arguments: lpFileName - name of input file
|
|
// fdwAccess - CreateFile access type - (e.g. GENERIC_READ)
|
|
// fdwShareMode - Share mode - (e.g. FILE_SHARE_READ)
|
|
// fdwCreate - Action to be taken - (e.g. OPEN_EXISTING)
|
|
//
|
|
// Returns: INT - LZFile struct table offset or WIN32 file HANDLE if
|
|
// successful. One of the LZERROR_ codes if unsuccessful.
|
|
//
|
|
INT
|
|
LZCreateFileW(
|
|
LPWSTR lpFileName,
|
|
DWORD fdwAccess,
|
|
DWORD fdwShareMode,
|
|
DWORD fdwCreate,
|
|
LPWSTR lpCompressedName)
|
|
{
|
|
HANDLE hWin32; // WIN32 file HANDLE returned from CreateFileW()
|
|
INT lzh; // LZ File struct ID returned from LZInit()
|
|
static WCHAR pszCompName[MAX_PATH]; // buffer for compressed name
|
|
|
|
lstrcpyW((LPWSTR)pszCompName, lpFileName);
|
|
|
|
// Just for Vlad, only try to open the compressed version of the original
|
|
// file name if we can't find the original file. All other errors get
|
|
// returned immediately.
|
|
|
|
hWin32 = CreateFileW(pszCompName, fdwAccess, fdwShareMode, NULL, fdwCreate,
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (hWin32 == INVALID_HANDLE_VALUE) {
|
|
DWORD dwErr = GetLastError();
|
|
|
|
if (dwErr == ERROR_FILE_NOT_FOUND) {
|
|
|
|
// Let's try to open the file of the corresponding compressed name.
|
|
MakeCompressedNameW((LPWSTR)pszCompName);
|
|
|
|
hWin32 = CreateFileW(pszCompName, fdwAccess, fdwShareMode,
|
|
NULL, fdwCreate, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
}
|
|
}
|
|
|
|
// Error opening file?
|
|
if (hWin32 == INVALID_HANDLE_VALUE) {
|
|
return(LZERROR_BADINHANDLE);
|
|
}
|
|
|
|
// Don't call LZinit() on files opened in other read only mode
|
|
|
|
if (fdwCreate != OPEN_EXISTING) {
|
|
lzh = ConvertWin32FHToDos((HFILE)((DWORD_PTR)hWin32));
|
|
if (lzh == LZERROR_GLOBALLOC) {
|
|
CloseHandle(hWin32);
|
|
}
|
|
|
|
return(lzh);
|
|
}
|
|
|
|
// File has been opened with read-only action - call LZInit() to detect
|
|
// whether or not it's an LZ file, and to create structures for expansion
|
|
// if it is an LZ file.
|
|
lzh = LZInit((INT)((DWORD_PTR)hWin32));
|
|
|
|
// Close DOS file handle if LZInit() is unsuccessful.
|
|
if (lzh < 0)
|
|
CloseHandle(hWin32);
|
|
|
|
// Pass real file name to caller
|
|
//
|
|
// we believe the caller have enough buffer.
|
|
//
|
|
if( lpCompressedName != NULL )
|
|
lstrcpyW(lpCompressedName,pszCompName);
|
|
|
|
// Return LZ struct ID or LZERROR_ code.
|
|
return(lzh);
|
|
}
|
|
|
|
|
|
/*
|
|
** int APIENTRY LZOpenFileA(LPCSTR lpFileName, LPOFSTRUCT lpReOpenBuf,
|
|
** WORD wStyle);
|
|
**
|
|
** Opens a file and sets up an LZFile struct for expanding it.
|
|
**
|
|
** Arguments: lpFileName - name of input file
|
|
** lpReOpenBuf - pointer to LPOFSTRUCT to be used by OpenFile()
|
|
** wStyle - OpenFile() action to take
|
|
**
|
|
** Returns: int - LZFile struct table offset or DOS file handle if
|
|
** successful. One of the LZERROR_ codes if unsuccessful.
|
|
**
|
|
** Globals: none
|
|
*/
|
|
INT APIENTRY LZOpenFileA(LPSTR lpFileName, LPOFSTRUCT lpReOpenBuf, WORD wStyle)
|
|
{
|
|
INT dosh, // DOS file handle returned from OpenFile()
|
|
lzh; // LZ File struct ID returned from LZInit()
|
|
CHAR pszCompName[MAX_PATH]; // buffer for compressed name
|
|
|
|
STRCPY((LPSTR)pszCompName, lpFileName);
|
|
|
|
// Just for Vlad, only try to open the compressed version of the original
|
|
// file name if we can't find the original file. All other errors get
|
|
// returned immediately.
|
|
|
|
if ((dosh = OpenFile(pszCompName, lpReOpenBuf, wStyle)) == -1 &&
|
|
lpReOpenBuf->nErrCode == DEE_FILENOTFOUND)
|
|
{
|
|
// Let's try to open the file of the corresponding compressed name.
|
|
MakeCompressedName(pszCompName);
|
|
|
|
dosh = (HFILE) MOpenFile((LPSTR)pszCompName, lpReOpenBuf, wStyle);
|
|
}
|
|
|
|
// Error opening file?
|
|
if (dosh == -1)
|
|
return(LZERROR_BADINHANDLE);
|
|
|
|
// Don't call LZinit() on files opened in other than O_RDONLY mode.
|
|
// Ignore the SHARE bits.
|
|
if ((wStyle & STYLE_MASK) != OF_READ) {
|
|
lzh = ConvertWin32FHToDos(dosh);
|
|
if (lzh == LZERROR_GLOBALLOC) {
|
|
FCLOSE(dosh);
|
|
}
|
|
return(lzh);
|
|
}
|
|
|
|
// File has been opened with OF_READ style - call LZInit() to detect
|
|
// whether or not it's an LZ file, and to create structures for expansion
|
|
// if it is an LZ file.
|
|
lzh = LZInit(dosh);
|
|
|
|
// Close DOS file handle if LZInit() is unsuccessful.
|
|
if (lzh < 0)
|
|
FCLOSE(dosh);
|
|
|
|
// Return LZ struct ID or LZERROR_ code.
|
|
return(lzh);
|
|
}
|
|
|
|
/*
|
|
** int APIENTRY LZOpenFileW(LPCWSTR lpFileName, LPOFSTRUCT lpReOpenBuf,
|
|
** WORD wStyle);
|
|
**
|
|
** Wide Character version of LZOpenFile. Converts the filename to
|
|
** the ANSI Character set and calls the ANSI version.
|
|
**
|
|
*/
|
|
INT APIENTRY LZOpenFileW(LPWSTR lpFileName, LPOFSTRUCT lpReOpenBuf, WORD wStyle)
|
|
{
|
|
UNICODE_STRING FileName;
|
|
ANSI_STRING AnsiString;
|
|
NTSTATUS StatusR;
|
|
CHAR szFileName[MAX_PATH];
|
|
|
|
|
|
FileName.Buffer = lpFileName;
|
|
FileName.Length = wcslen(lpFileName)*sizeof(WCHAR);
|
|
FileName.MaximumLength = FileName.Length + sizeof(WCHAR);
|
|
|
|
AnsiString.Buffer = szFileName;
|
|
AnsiString.MaximumLength = MAX_PATH;
|
|
StatusR = RtlUnicodeStringToAnsiString(&AnsiString, &FileName, FALSE);
|
|
if (!NT_SUCCESS(StatusR))
|
|
return LZERROR_GLOBALLOC;
|
|
|
|
return(LZOpenFileA(szFileName, lpReOpenBuf, wStyle));
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
** LONG APIENTRY LZSeek(int oLZFile, long lSeekTo, int nMode);
|
|
**
|
|
** Works like _llseek(), but in the expanded image of a compressed file,
|
|
** without expanding the compressed file to disk.
|
|
**
|
|
** Arguments: oLZFile - source LZFile struct identifier or DOS file handle
|
|
** lSeekTo - number of bytes past nMode target to seek
|
|
** nMode - seek mode as in _llseek()
|
|
**
|
|
** Returns: LONG - Offset of the seek target if successful. One of the
|
|
** LZERROR_ codes if unsuccessful.
|
|
**
|
|
** Globals: none
|
|
*/
|
|
LONG APIENTRY
|
|
LZSeek(
|
|
INT oLZFile,
|
|
LONG lSeekTo,
|
|
INT nMode)
|
|
{
|
|
HANDLE hSourceStruct; // handle to LZFile struct
|
|
LZFile *lpLZ; // pointer to LZFile struct
|
|
LONG lExpSeekTarget; // target seek offset
|
|
|
|
// Check input LZFile struct indentifier / DOS file handle.
|
|
if (oLZFile < 0 || oLZFile >= LZ_TABLE_BIAS + MAX_LZFILES)
|
|
return(LZERROR_BADINHANDLE);
|
|
|
|
// We were passed a regular DOS file handle, so just do an _llseek() on it.
|
|
if (oLZFile < LZ_TABLE_BIAS)
|
|
return(FSEEK(ConvertDosFHToWin32(oLZFile), lSeekTo, nMode));
|
|
|
|
// We're dealing with a compressed file. Get the associated LZFile struct.
|
|
hSourceStruct = rghLZFileTable[oLZFile - LZ_TABLE_BIAS];
|
|
|
|
if ((lpLZ = (LZFile *)GlobalLock(hSourceStruct)) == NULL)
|
|
return(LZERROR_GLOBLOCK);
|
|
|
|
// Figure out what our seek target is.
|
|
if (nMode == SEEK_SET)
|
|
lExpSeekTarget = 0L;
|
|
else if (nMode == SEEK_CUR)
|
|
lExpSeekTarget = lpLZ->lCurSeekPos;
|
|
else if (nMode == SEEK_END)
|
|
lExpSeekTarget = (LONG)lpLZ->cbulUncompSize;
|
|
else
|
|
{
|
|
GlobalUnlock(hSourceStruct);
|
|
return(LZERROR_BADVALUE);
|
|
}
|
|
|
|
// Add bias.
|
|
lExpSeekTarget += lSeekTo;
|
|
|
|
// Make sure the desired expanded file position is in the expanded file
|
|
// bounds. It's only an error to seek before the beginning of the file;
|
|
// it's not an error to seek after the end of the file, as in _llseek().
|
|
if (lExpSeekTarget < 0L)
|
|
{
|
|
GlobalUnlock(hSourceStruct);
|
|
return(LZERROR_BADVALUE);
|
|
}
|
|
|
|
// Seek target is ok. Set the file pointer for the expanded file image.
|
|
lpLZ->lCurSeekPos = lExpSeekTarget;
|
|
|
|
GlobalUnlock(hSourceStruct);
|
|
|
|
// Return the offset of the seek target.
|
|
return(lExpSeekTarget);
|
|
}
|
|
|
|
|
|
/*
|
|
** int APIENTRY LZRead(int oLZFile, LPSTR lpBuf, int nCount);
|
|
**
|
|
** Works like _lread(), but in the expanded image of a compressed file,
|
|
** without expanding the compressed file to disk.
|
|
**
|
|
** Arguments: oLZFile - source LZFile struct identifier or DOS file handle
|
|
** lpBuf - pointer to destination buffer for bytes read
|
|
** nCount - number of bytes to read
|
|
**
|
|
** Returns: int - Number of bytes copied to destination buffer if
|
|
** successful. One of the LZERROR_ codes if unsuccessful.
|
|
**
|
|
** Globals: none
|
|
*/
|
|
INT APIENTRY LZRead(INT oLZFile, LPSTR lpBuf, INT nCount)
|
|
{
|
|
INT f;
|
|
HANDLE hSourceStruct; // handle to LZFile struct
|
|
LZFile *lpLZ; // pointer to LZFile struct
|
|
|
|
INT cbWritten = 0, // total number of bytes copied to
|
|
// destination buffer
|
|
cbCopied, // number of bytes copied to destination
|
|
// buffer during each read iteration
|
|
iCurReadPos, // current read offset in expanded buffer
|
|
nNumExpBufBytes; // number of bytes in expanded data buffer
|
|
LONG lNextDecodeTarget, // expanded file image read target for decoding
|
|
lStartCopyOffset, // expanded file buffer offset where we should
|
|
// start copying to destination buffer (cast
|
|
// to iCurReadPos when this start position
|
|
// is actually in the buffer)
|
|
lNextExpEndOffset; // expanded file offset of the start of the
|
|
// next desired block of expanded data
|
|
BOOL bRestartDecoding; // flag indicating whether or not decoding
|
|
// needs to be restarted, set to TRUE when
|
|
// the current seek position is smaller than
|
|
// the offset of the beginning of the
|
|
// expanded file buffer
|
|
BYTE *lpbyteBuf; // byte pointer version of lpBuf
|
|
|
|
LONG lExpBufStart;
|
|
LONG lExpBufEnd;
|
|
|
|
PLZINFO pLZI;
|
|
|
|
// Check input LZFile struct indentifier / DOS file handle.
|
|
if (oLZFile < 0 || oLZFile >= LZ_TABLE_BIAS + MAX_LZFILES)
|
|
return(LZERROR_BADINHANDLE);
|
|
|
|
// Can't read a negative number of bytes.
|
|
if (nCount < 0)
|
|
return(LZERROR_BADVALUE);
|
|
|
|
// We were passed a regular DOS file handle, so just do an _lread() on it.
|
|
if (oLZFile < LZ_TABLE_BIAS)
|
|
return(FREAD(ConvertDosFHToWin32(oLZFile), lpBuf, nCount));
|
|
|
|
// We're dealing with a compressed file. Get the associated LZFile struct.
|
|
hSourceStruct = rghLZFileTable[oLZFile - LZ_TABLE_BIAS];
|
|
|
|
if ((lpLZ = (LZFile *)GlobalLock(hSourceStruct)) == NULL)
|
|
return(LZERROR_GLOBLOCK);
|
|
|
|
if (!(pLZI = lpLZ->pLZI)) {
|
|
// Initialize buffers
|
|
lpLZ->pLZI = InitGlobalBuffers(EXP_BUF_LEN, MAX_RING_BUF_LEN, IN_BUF_LEN + 1);
|
|
|
|
if (!(pLZI = lpLZ->pLZI)) {
|
|
return(LZERROR_GLOBALLOC);
|
|
}
|
|
|
|
ResetBuffers();
|
|
}
|
|
|
|
lExpBufStart = pLZI->cblOutSize - (LONG)(pLZI->pbyteOutBuf - pLZI->rgbyteOutBuf);
|
|
lExpBufEnd = (LONG)(pLZI->pbyteOutBufEnd - pLZI->rgbyteOutBuf);
|
|
|
|
// Do we need to restart decoding?
|
|
if (! (lpLZ->wFlags & LZF_INITIALIZED))
|
|
{
|
|
lpLZ->wFlags |= LZF_INITIALIZED;
|
|
bRestartDecoding = TRUE;
|
|
}
|
|
else if (lpLZ->lCurSeekPos < lExpBufStart)
|
|
bRestartDecoding = TRUE;
|
|
else
|
|
bRestartDecoding = FALSE;
|
|
|
|
// Set up byte pointer version of lpBuf.
|
|
lpbyteBuf = lpBuf;
|
|
|
|
// Copy bytes until buffer is filled or EOF in expanded file image is
|
|
// reached.
|
|
while (nCount > 0 && lpLZ->lCurSeekPos < (LONG)lpLZ->cbulUncompSize)
|
|
{
|
|
/* How many expanded data bytes are in the expanded data buffer?
|
|
* (pbyteOutBuf points AFTER the last valid byte in rgbyteOutBuf[].)
|
|
*/
|
|
nNumExpBufBytes = (INT)(pLZI->pbyteOutBuf - pLZI->rgbyteOutBuf);
|
|
|
|
/* Is the start of the desired expanded data currently in the bounds of
|
|
* the expanded data buffer?
|
|
*/
|
|
lStartCopyOffset = lpLZ->lCurSeekPos - lExpBufStart;
|
|
if (lStartCopyOffset < lExpBufEnd)
|
|
/* It's ok to set iCurReadPos to a negative value here, since we
|
|
* will only use expanded data from the expanded data buffer if
|
|
* iCurReadPos is non-negative.
|
|
*/
|
|
iCurReadPos = (INT)lStartCopyOffset;
|
|
else
|
|
iCurReadPos = -1;
|
|
|
|
/* Now, if iCurReadPos > 0, some of the expanded data in the expanded
|
|
* data buffer should be copied to the caller's buffer. If not, we
|
|
* need to continue expanding or restart expanding.
|
|
*/
|
|
if (iCurReadPos >= 0)
|
|
{
|
|
/* Copy available expanded data from expanded data buffer. */
|
|
for (cbCopied = 0;
|
|
iCurReadPos < nNumExpBufBytes && nCount > 0;
|
|
cbCopied++, nCount--)
|
|
*lpbyteBuf++ = pLZI->rgbyteOutBuf[iCurReadPos++];
|
|
|
|
// Update expanded file pointer.
|
|
lpLZ->lCurSeekPos += (LONG)cbCopied;
|
|
|
|
// Keep track of bytes written to buffer.
|
|
cbWritten += cbCopied;
|
|
}
|
|
|
|
/* Expand more data, restarting the expansion process first if
|
|
* necessary.
|
|
*/
|
|
if (nCount > 0 && lpLZ->lCurSeekPos < (LONG)lpLZ->cbulUncompSize)
|
|
{
|
|
/* If we get here, we've copied all the available expanded data out
|
|
* of rgbyteOutBuf[], through pbyteOutBuf, and we need to expand
|
|
* more data.
|
|
*/
|
|
|
|
/* Where is the end of the next desired expanded data block? */
|
|
if (bRestartDecoding)
|
|
{
|
|
/* Seek back to start of target data, allowing for buffer
|
|
* overflow.
|
|
*/
|
|
lNextExpEndOffset = lpLZ->lCurSeekPos - MAX_OVERRUN;
|
|
|
|
/* Don't try to read before offset 0! */
|
|
if (lNextExpEndOffset < 0)
|
|
lNextExpEndOffset = 0;
|
|
}
|
|
else
|
|
/* Continue decoding. */
|
|
lNextExpEndOffset = lExpBufStart
|
|
+ (LONG)nNumExpBufBytes
|
|
+ lExpBufEnd
|
|
- MAX_OVERRUN;
|
|
|
|
/* How much farther should we expand? The target decoding offset
|
|
* should be the smallest expanded file offset of the following:
|
|
*
|
|
* 1) the last byte in the largest expanded data block that will
|
|
* safely fit in the expanded data buffer, while guaranteeing
|
|
* that the start of this block is also in the expanded data
|
|
* buffer
|
|
* 2) the last requested expanded data byte
|
|
* 3) the last byte in the expanded file
|
|
*/
|
|
lNextDecodeTarget = MIN(lNextExpEndOffset,
|
|
MIN(lpLZ->lCurSeekPos + (LONG)nCount,
|
|
(LONG)lpLZ->cbulUncompSize - 1L));
|
|
|
|
// Reset expanded data buffer to empty state.
|
|
pLZI->pbyteOutBuf = pLZI->rgbyteOutBuf;
|
|
|
|
// Refill rgbyteOutBuf[] with expanded data.
|
|
switch (lpLZ->byteAlgorithm)
|
|
{
|
|
case ALG_FIRST:
|
|
f = LZDecode(lpLZ->dosh, NO_DOSH, lNextDecodeTarget,
|
|
bRestartDecoding, TRUE, pLZI);
|
|
break;
|
|
|
|
default:
|
|
f = LZERROR_UNKNOWNALG;
|
|
break;
|
|
}
|
|
|
|
// Did the decoding go ok?
|
|
if (f != TRUE)
|
|
{
|
|
// Uh oh. Something went wrong.
|
|
GlobalUnlock(hSourceStruct);
|
|
return(f);
|
|
}
|
|
|
|
/* Now how many expanded data bytes are in the expanded data buffer?
|
|
* (pbyteOutBuf points AFTER the last valid byte in rgbyteOutBuf[].)
|
|
*/
|
|
#if DEBUG
|
|
printf("pbyteOutBuf: 0x%x, rgbyteOutBuf: 0x%x \n", pLZI->pbyteOutBuf, pLZI->rgbyteOutBuf);
|
|
#endif
|
|
|
|
nNumExpBufBytes = (INT)(pLZI->pbyteOutBuf - pLZI->rgbyteOutBuf);
|
|
|
|
/* Check to make sure we actually read some bytes. */
|
|
if (nNumExpBufBytes <= 0)
|
|
{
|
|
GlobalUnlock(hSourceStruct);
|
|
return(LZERROR_READ);
|
|
}
|
|
|
|
/* What is the offset of the start of the expanded data buffer in
|
|
* the expanded file image?
|
|
*/
|
|
lExpBufStart = pLZI->cblOutSize - (LONG)nNumExpBufBytes;
|
|
|
|
/* Did LZDecode() satisfy the read request, or did the compressed
|
|
* file end prematurely?
|
|
*/
|
|
if (pLZI->cblOutSize < lNextDecodeTarget)
|
|
{
|
|
/* Oh oh. lNextDecodeTarget cannot exceed the expanded file
|
|
* bounds, so the compressed file must have ended prematurely.
|
|
*/
|
|
GlobalUnlock(hSourceStruct);
|
|
return(LZERROR_READ);
|
|
}
|
|
|
|
// Reset flag so we continue decoding where we left off.
|
|
bRestartDecoding = FALSE;
|
|
}
|
|
}
|
|
|
|
GlobalUnlock(hSourceStruct);
|
|
|
|
// Return number of bytes copied to destination buffer.
|
|
return(cbWritten);
|
|
}
|
|
|
|
//
|
|
// VOID LZCloseFile(INT oLZFile);
|
|
//
|
|
// Close a file and free the associated LZFile struct.
|
|
//
|
|
// Arguments: oLZFile - source LZFile struct identifier or WIN32 file handle
|
|
//
|
|
// Returns: VOID
|
|
//
|
|
// Globals: rghLZFileTable[] entry cleared.
|
|
//
|
|
|
|
VOID
|
|
LZCloseFile(
|
|
INT oLZFile)
|
|
{
|
|
HANDLE hSourceStruct; // handle to LZFile struct
|
|
LZFile *lpLZ; // pointer to LZFile struct
|
|
|
|
// Check input LZFile struct indentifier / DOS file handle.
|
|
if (oLZFile < 0 || oLZFile >= LZ_TABLE_BIAS + MAX_LZFILES)
|
|
return;
|
|
|
|
// We were passed a regular DOS file handle, so just close it.
|
|
if (oLZFile < LZ_TABLE_BIAS) {
|
|
CloseHandle((HANDLE)IntToPtr(ConvertDosFHToWin32(oLZFile)));
|
|
// also need to clean out the file array entry
|
|
fhWin32File[oLZFile] = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
// We're dealing with a compressed file. Get the associated LZFile struct.
|
|
hSourceStruct = rghLZFileTable[oLZFile - LZ_TABLE_BIAS];
|
|
|
|
// Clear rghLZFIleTable[] entry.
|
|
rghLZFileTable[oLZFile - LZ_TABLE_BIAS] = NULL;
|
|
|
|
// Close the file and free the associated LZFile struct.
|
|
if ((lpLZ = (LZFile *)GlobalLock(hSourceStruct)) != NULL)
|
|
{
|
|
CloseHandle((HANDLE)IntToPtr(lpLZ->dosh));
|
|
|
|
if (lpLZ->pLZI) {
|
|
FreeGlobalBuffers(lpLZ->pLZI);
|
|
}
|
|
|
|
GlobalUnlock(hSourceStruct);
|
|
|
|
GlobalFree(hSourceStruct);
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** VOID APIENTRY LZClose(int oLZFile);
|
|
**
|
|
** Close a file and free the associated LZFile struct.
|
|
**
|
|
** Arguments: oLZFile - source LZFile struct identifier or DOS file handle
|
|
**
|
|
** Returns: VOID
|
|
**
|
|
** Globals: rghLZFileTable[] entry cleared.
|
|
*/
|
|
VOID APIENTRY LZClose(INT oLZFile)
|
|
{
|
|
HANDLE hSourceStruct; // handle to LZFile struct
|
|
LZFile *lpLZ; // pointer to LZFile struct
|
|
|
|
// Check input LZFile struct indentifier / DOS file handle.
|
|
if (oLZFile < 0 || oLZFile >= LZ_TABLE_BIAS + MAX_LZFILES)
|
|
return;
|
|
|
|
// We were passed a regular DOS file handle, so just close it.
|
|
if (oLZFile < LZ_TABLE_BIAS)
|
|
{
|
|
FCLOSE(ConvertDosFHToWin32(oLZFile));
|
|
/* also need to clean out the file array entry */
|
|
fhWin32File[oLZFile] = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
// We're dealing with a compressed file. Get the associated LZFile struct.
|
|
hSourceStruct = rghLZFileTable[oLZFile - LZ_TABLE_BIAS];
|
|
|
|
// Clear rghLZFIleTable[] entry.
|
|
rghLZFileTable[oLZFile - LZ_TABLE_BIAS] = NULL;
|
|
|
|
// Close the file and free the associated LZFile struct.
|
|
if ((lpLZ = (LZFile *)GlobalLock(hSourceStruct)) != NULL)
|
|
{
|
|
FCLOSE(lpLZ->dosh);
|
|
|
|
if (lpLZ->pLZI) {
|
|
FreeGlobalBuffers(lpLZ->pLZI);
|
|
}
|
|
|
|
GlobalUnlock(hSourceStruct);
|
|
|
|
GlobalFree(hSourceStruct);
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* WIN32 MODS */
|
|
|
|
INT ConvertWin32FHToDos(HFILE DoshSource)
|
|
{
|
|
INT x;
|
|
|
|
if (!fCSInit) {
|
|
if (!NT_SUCCESS(RtlInitializeCriticalSection(&semTable))) {
|
|
return LZERROR_GLOBALLOC;
|
|
}
|
|
fCSInit = TRUE;
|
|
}
|
|
|
|
/* here we are given an NT file handle, need save this into
|
|
* fhWin32File[], test for overflow, also need see
|
|
* if there is a free slot in the array */
|
|
|
|
/* If error, return greater than MAX_LZFILES */
|
|
|
|
RtlEnterCriticalSection(&semTable);
|
|
|
|
/* walk array, looking for a free slot (free slot = 0) */
|
|
for(x = 0; x < MAX_LZFILES; x++){
|
|
if(fhWin32File[x] == 0)
|
|
break;
|
|
}
|
|
if(x < MAX_LZFILES){
|
|
/* no overflow, save into array*/
|
|
fhWin32File[x] = DoshSource;
|
|
}
|
|
else{
|
|
x = LZERROR_GLOBALLOC;
|
|
}
|
|
|
|
RtlLeaveCriticalSection(&semTable);
|
|
|
|
return(x);
|
|
|
|
}
|
|
|
|
|
|
HFILE ConvertDosFHToWin32(INT DoshSource)
|
|
{
|
|
|
|
/* here, we are given the pseudo Dos File Handle, need convert to
|
|
* real file handle, for use by API.
|
|
*/
|
|
|
|
if (DoshSource >= MAX_LZFILES || DoshSource < 0 ||
|
|
fhWin32File[DoshSource] == 0) {
|
|
return (HFILE)DoshSource;
|
|
}
|
|
else{
|
|
return(fhWin32File[DoshSource]);
|
|
}
|
|
|
|
}
|