#include "pch.h"
#include "compress.h"
#include "mrcicode.h"

#define COMPRESS_SIG            0x434F4D50  //COMP
#define COMPRESS_CONT_SIG       0x434F4D43  //COMC
#define COMPRESS_NEWFILE        0x434F4D46  //COMF
#define COMPRESS_BUFFER_SIZE    0x8000      //32K
#define COMPRESS_DEFAULT_SIZE   0x7FFFFFFF  //2GB

BOOL g_ErrorMode = FALSE;

unsigned
CompressData(
    IN  CompressionType Type,
    IN  PBYTE           Data,
    IN  unsigned        DataSize,
    OUT PBYTE           CompressedData,
    IN  unsigned        BufferSize
    )
{
    unsigned u;

    switch(Type) {

    case CompressNone:
    default:
        //
        // Force caller to do something intelligent, such as
        // writing directly out of the uncompressed buffer.
        // This avoids an extra memory move.
        //
        u = (unsigned)(-1);
        break;

    case CompressMrci1:
        u = Mrci1MaxCompress(Data,DataSize,CompressedData,BufferSize);
        break;

    case CompressMrci2:
        u = Mrci2MaxCompress(Data,DataSize,CompressedData,BufferSize);
        break;
    }

    return(u);
}


unsigned
DecompressData(
    IN  CompressionType Type,
    IN  PBYTE           CompressedData,
    IN  unsigned        CompressedDataSize,
    OUT PBYTE           DecompressedData,
    IN  unsigned        BufferSize
    )
{
    unsigned u;

    switch(Type) {

    case CompressNone:
        if(BufferSize >= CompressedDataSize) {
            memmove(DecompressedData,CompressedData,CompressedDataSize);
            u = CompressedDataSize;
        } else {
            u = (unsigned)(-1);
        }
        break;

    case CompressMrci1:
        u = Mrci1Decompress(CompressedData,CompressedDataSize,DecompressedData,BufferSize);
        break;

    case CompressMrci2:
        u = Mrci2Decompress(CompressedData,CompressedDataSize,DecompressedData,BufferSize);
        break;

    default:
        u = (unsigned)(-1);
        break;
    }

    return(u);
}

VOID
CompressCleanupHandleA (
    IN OUT  PCOMPRESS_HANDLEA CompressedHandle
    )
{
    if (CompressedHandle) {
        if (CompressedHandle->ReadBuffer) {
            MemFree (g_hHeap, 0, CompressedHandle->ReadBuffer);
        }
        if (CompressedHandle->ExtraBuffer) {
            MemFree (g_hHeap, 0, CompressedHandle->ExtraBuffer);
        }
        if (CompressedHandle->CompBuffer) {
            MemFree (g_hHeap, 0, CompressedHandle->CompBuffer);
        }
        if (CompressedHandle->StorePath) {
            FreePathStringA (CompressedHandle->StorePath);
        }
        if (CompressedHandle->MainFilePattern) {
            FreePathStringA (CompressedHandle->MainFilePattern);
        }
        if ((CompressedHandle->CurrFileHandle) &&
            (CompressedHandle->CurrFileHandle != INVALID_HANDLE_VALUE)
            ) {
            CloseHandle (CompressedHandle->CurrFileHandle);
        }
        ZeroMemory (CompressedHandle, sizeof (COMPRESS_HANDLEA));
    }
}

VOID
CompressCleanupHandleW (
    IN OUT  PCOMPRESS_HANDLEW CompressedHandle
    )
{
    if (CompressedHandle) {
        if (CompressedHandle->ReadBuffer) {
            MemFree (g_hHeap, 0, CompressedHandle->ReadBuffer);
        }
        if (CompressedHandle->ExtraBuffer) {
            MemFree (g_hHeap, 0, CompressedHandle->ExtraBuffer);
        }
        if (CompressedHandle->CompBuffer) {
            MemFree (g_hHeap, 0, CompressedHandle->CompBuffer);
        }
        if (CompressedHandle->StorePath) {
            FreePathStringW (CompressedHandle->StorePath);
        }
        if (CompressedHandle->MainFilePattern) {
            FreePathStringW (CompressedHandle->MainFilePattern);
        }
        if ((CompressedHandle->CurrFileHandle) &&
            (CompressedHandle->CurrFileHandle != INVALID_HANDLE_VALUE)
            ) {
            CloseHandle (CompressedHandle->CurrFileHandle);
        }
        ZeroMemory (CompressedHandle, sizeof (COMPRESS_HANDLEW));
    }
}

BOOL
CompressCreateHandleA (
    IN      PCSTR StorePath,
    IN      PCSTR MainFilePattern,
    IN      UINT StartIndex,
    IN      LONGLONG MaxFileSize,
    OUT     PCOMPRESS_HANDLEA CompressedHandle
    )
{
    CHAR currFile [MAX_PATH];
    PCSTR currFullPath = NULL;
    DWORD signature = COMPRESS_SIG;
    BOOL result = FALSE;

    __try {

        ZeroMemory (CompressedHandle, sizeof (COMPRESS_HANDLEA));
        if (StartIndex == 0) {
            CompressedHandle->CurrFileIndex = 1;
        } else {
            CompressedHandle->CurrFileIndex = StartIndex;
        }
        CompressedHandle->FirstFileIndex = CompressedHandle->CurrFileIndex;
        if (MaxFileSize == 0) {
            CompressedHandle->MaxFileSize = COMPRESS_DEFAULT_SIZE;
        } else {
            CompressedHandle->MaxFileSize = MaxFileSize;
        }
        CompressedHandle->StorePath = DuplicatePathStringA (StorePath, 0);
        if (!CompressedHandle->StorePath) {
            __leave;
        }
        CompressedHandle->MainFilePattern = DuplicatePathStringA (MainFilePattern, 0);
        if (!CompressedHandle->MainFilePattern) {
            __leave;
        }
        wsprintfA (currFile, CompressedHandle->MainFilePattern, CompressedHandle->CurrFileIndex);
        currFullPath = JoinPathsA (CompressedHandle->StorePath, currFile);
        CompressedHandle->CurrFileHandle = BfCreateFileA (currFullPath);
        if ((CompressedHandle->CurrFileHandle == NULL) ||
            (CompressedHandle->CurrFileHandle == INVALID_HANDLE_VALUE)
            ) {
            __leave;
        }
        FreePathStringA (currFullPath);
        currFullPath = NULL;

        // write the signature
        if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
            __leave;
        }
        CompressedHandle->CurrFileSize += sizeof (DWORD);

        // reserve room for writing how many files we stored
        if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&CompressedHandle->FilesStored), sizeof (LONGLONG))) {
            __leave;
        }
        CompressedHandle->CurrFileSize += sizeof (LONGLONG);

        CompressedHandle->ReadBuffer = MemAlloc (g_hHeap, 0, COMPRESS_BUFFER_SIZE + 2 * sizeof (USHORT));
        if (!CompressedHandle->ReadBuffer) {
            __leave;
        }

        CompressedHandle->CompBuffer = MemAlloc (g_hHeap, 0, COMPRESS_BUFFER_SIZE + 2 * sizeof (USHORT));
        if (!CompressedHandle->CompBuffer) {
            __leave;
        }

        CompressedHandle->ExtraBuffer = MemAlloc (g_hHeap, 0, COMPRESS_BUFFER_SIZE + 2 * sizeof (USHORT));
        if (!CompressedHandle->ExtraBuffer) {
            __leave;
        }

        result = TRUE;
    }
    __finally {
        PushError ();
        if (currFullPath) {
            FreePathStringA (currFullPath);
            currFullPath = NULL;
        }
        if (!result) {
            CompressCleanupHandleA (CompressedHandle);
        }
        PopError ();
    }

    return result;
}

BOOL
CompressCreateHandleW (
    IN      PCWSTR StorePath,
    IN      PCWSTR MainFilePattern,
    IN      UINT StartIndex,
    IN      LONGLONG MaxFileSize,
    OUT     PCOMPRESS_HANDLEW CompressedHandle
    )
{
    WCHAR currFile [MAX_PATH];
    PCWSTR currFullPath = NULL;
    DWORD signature = COMPRESS_SIG;
    BOOL result = FALSE;

    __try {

        ZeroMemory (CompressedHandle, sizeof (COMPRESS_HANDLEW));
        if (StartIndex == 0) {
            CompressedHandle->CurrFileIndex = 1;
        } else {
            CompressedHandle->CurrFileIndex = StartIndex;
        }
        CompressedHandle->FirstFileIndex = CompressedHandle->CurrFileIndex;
        if (MaxFileSize == 0) {
            CompressedHandle->MaxFileSize = COMPRESS_DEFAULT_SIZE;
        } else {
            CompressedHandle->MaxFileSize = MaxFileSize;
        }
        CompressedHandle->StorePath = DuplicatePathStringW (StorePath, 0);
        if (!CompressedHandle->StorePath) {
            __leave;
        }
        CompressedHandle->MainFilePattern = DuplicatePathStringW (MainFilePattern, 0);
        if (!CompressedHandle->MainFilePattern) {
            __leave;
        }
        wsprintfW (currFile, CompressedHandle->MainFilePattern, CompressedHandle->CurrFileIndex);
        currFullPath = JoinPathsW (CompressedHandle->StorePath, currFile);
        CompressedHandle->CurrFileHandle = BfCreateFileW (currFullPath);
        if ((CompressedHandle->CurrFileHandle == NULL) ||
            (CompressedHandle->CurrFileHandle == INVALID_HANDLE_VALUE)
            ) {
            __leave;
        }
        FreePathStringW (currFullPath);
        currFullPath = NULL;

        // write the signature
        if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
            __leave;
        }
        CompressedHandle->CurrFileSize += sizeof (DWORD);

        // reserve room for writing how many files we stored
        if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&CompressedHandle->FilesStored), sizeof (LONGLONG))) {
            __leave;
        }
        CompressedHandle->CurrFileSize += sizeof (LONGLONG);

        CompressedHandle->ReadBuffer = MemAlloc (g_hHeap, 0, COMPRESS_BUFFER_SIZE + 2 * sizeof (USHORT));
        if (!CompressedHandle->ReadBuffer) {
            __leave;
        }

        CompressedHandle->CompBuffer = MemAlloc (g_hHeap, 0, COMPRESS_BUFFER_SIZE + 2 * sizeof (USHORT));
        if (!CompressedHandle->CompBuffer) {
            __leave;
        }

        CompressedHandle->ExtraBuffer = MemAlloc (g_hHeap, 0, COMPRESS_BUFFER_SIZE + 2 * sizeof (USHORT));
        if (!CompressedHandle->ExtraBuffer) {
            __leave;
        }

        result = TRUE;
    }
    __finally {
        PushError ();
        if (currFullPath) {
            FreePathStringW (currFullPath);
            currFullPath = NULL;
        }
        if (!result) {
            CompressCleanupHandleW (CompressedHandle);
        }
        PopError ();
    }

    return result;
}

BOOL
pPrepareNextFileA (
    IN OUT  PCOMPRESS_HANDLEA CompressedHandle,
    IN      BOOL ReadOnly
    )
{
    CHAR currFile [MAX_PATH];
    PCSTR currFullPath = NULL;
    DWORD signature = COMPRESS_SIG;
    LONGLONG contSig = COMPRESS_CONT_SIG;
    BOOL result = FALSE;

    __try {

        if ((CompressedHandle->CurrFileHandle == NULL) ||
            (CompressedHandle->CurrFileHandle == INVALID_HANDLE_VALUE)
            ) {
            __leave;
        }

        if (!CloseHandle (CompressedHandle->CurrFileHandle)) {
            __leave;
        }

        CompressedHandle->CurrFileSize = 0;

        CompressedHandle->CurrFileIndex ++;

        wsprintfA (currFile, CompressedHandle->MainFilePattern, CompressedHandle->CurrFileIndex);
        currFullPath = JoinPathsA (CompressedHandle->StorePath, currFile);
        if (ReadOnly) {
            CompressedHandle->CurrFileHandle = BfOpenReadFileA (currFullPath);
        } else {
            CompressedHandle->CurrFileHandle = BfCreateFileA (currFullPath);
        }
        if ((CompressedHandle->CurrFileHandle == NULL) ||
            (CompressedHandle->CurrFileHandle == INVALID_HANDLE_VALUE)
            ) {
            __leave;
        }
        FreePathStringA (currFullPath);
        currFullPath = NULL;

        if (ReadOnly) {
            // read the signature
            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
                __leave;
            }
            CompressedHandle->CurrFileSize += sizeof (DWORD);

            if (signature != COMPRESS_SIG) {
                SetLastError (ERROR_INVALID_DATA);
                __leave;
            }

            // read special continuation signature
            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&contSig), sizeof (LONGLONG))) {
                __leave;
            }
            CompressedHandle->CurrFileSize += sizeof (LONGLONG);

            if (CompressedHandle->CurrFileIndex > 1) {
                if (contSig != COMPRESS_CONT_SIG) {
                    SetLastError (ERROR_INVALID_DATA);
                    __leave;
                }
            }
        } else {
            // write the signature
            if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
                __leave;
            }
            CompressedHandle->CurrFileSize += sizeof (DWORD);

            // write special continuation signature
            if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&contSig), sizeof (LONGLONG))) {
                __leave;
            }
            CompressedHandle->CurrFileSize += sizeof (LONGLONG);
        }

        result = TRUE;
    }
    __finally {
        PushError ();
        if (currFullPath) {
            FreePathStringA (currFullPath);
            currFullPath = NULL;
        }
        PopError ();
    }

    return result;
}

BOOL
pPrepareNextFileW (
    IN OUT  PCOMPRESS_HANDLEW CompressedHandle,
    IN      BOOL ReadOnly
    )
{
    WCHAR currFile [MAX_PATH];
    PCWSTR currFullPath = NULL;
    DWORD signature = COMPRESS_SIG;
    LONGLONG contSig = COMPRESS_CONT_SIG;
    BOOL result = FALSE;

    __try {

        if ((CompressedHandle->CurrFileHandle == NULL) ||
            (CompressedHandle->CurrFileHandle == INVALID_HANDLE_VALUE)
            ) {
            __leave;
        }

        if (!CloseHandle (CompressedHandle->CurrFileHandle)) {
            __leave;
        }

        CompressedHandle->CurrFileSize = 0;

        CompressedHandle->CurrFileIndex ++;

        wsprintfW (currFile, CompressedHandle->MainFilePattern, CompressedHandle->CurrFileIndex);
        currFullPath = JoinPathsW (CompressedHandle->StorePath, currFile);
        if (ReadOnly) {
            CompressedHandle->CurrFileHandle = BfOpenReadFileW (currFullPath);
        } else {
            CompressedHandle->CurrFileHandle = BfCreateFileW (currFullPath);
        }
        if ((CompressedHandle->CurrFileHandle == NULL) ||
            (CompressedHandle->CurrFileHandle == INVALID_HANDLE_VALUE)
            ) {
            __leave;
        }
        FreePathStringW (currFullPath);
        currFullPath = NULL;

        if (ReadOnly) {
            // read the signature
            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
                __leave;
            }
            CompressedHandle->CurrFileSize += sizeof (DWORD);

            if (signature != COMPRESS_SIG) {
                SetLastError (ERROR_INVALID_DATA);
                __leave;
            }

            // read special continuation signature
            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&contSig), sizeof (LONGLONG))) {
                __leave;
            }
            CompressedHandle->CurrFileSize += sizeof (LONGLONG);

            if (CompressedHandle->CurrFileIndex > 1) {
                if (contSig != COMPRESS_CONT_SIG) {
                    SetLastError (ERROR_INVALID_DATA);
                    __leave;
                }
            }
        } else {
            // write the signature
            if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
                __leave;
            }
            CompressedHandle->CurrFileSize += sizeof (DWORD);

            // write special continuation signature
            if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&contSig), sizeof (LONGLONG))) {
                __leave;
            }
            CompressedHandle->CurrFileSize += sizeof (LONGLONG);
        }

        result = TRUE;
    }
    __finally {
        PushError ();
        if (currFullPath) {
            FreePathStringW (currFullPath);
            currFullPath = NULL;
        }
        PopError ();
    }

    return result;
}

BOOL
pDeleteNextFilesA (
    IN      PCOMPRESS_HANDLEA CompressedHandle,
    IN      UINT SavedIndex
    )
{
    CHAR currFile [MAX_PATH];
    PCSTR currFullPath = NULL;

    while (TRUE) {
        wsprintfA (currFile, CompressedHandle->MainFilePattern, SavedIndex);
        currFullPath = JoinPathsA (CompressedHandle->StorePath, currFile);
        if (currFullPath) {
            if (DoesFileExistA (currFullPath)) {
                DeleteFileA (currFullPath);
            } else {
                break;
            }
            FreePathStringA (currFullPath);
            currFullPath = NULL;
        }
        SavedIndex ++;
    }
    if (currFullPath) {
        FreePathStringA (currFullPath);
        currFullPath = NULL;
    }
    return TRUE;
}

BOOL
pDeleteNextFilesW (
    IN      PCOMPRESS_HANDLEW CompressedHandle,
    IN      UINT SavedIndex
    )
{
    WCHAR currFile [MAX_PATH];
    PCWSTR currFullPath = NULL;

    while (TRUE) {
        wsprintfW (currFile, CompressedHandle->MainFilePattern, SavedIndex);
        currFullPath = JoinPathsW (CompressedHandle->StorePath, currFile);
        if (currFullPath) {
            if (DoesFileExistW (currFullPath)) {
                DeleteFileW (currFullPath);
            } else {
                break;
            }
            FreePathStringW (currFullPath);
            currFullPath = NULL;
        }
        SavedIndex ++;
    }
    if (currFullPath) {
        FreePathStringW (currFullPath);
        currFullPath = NULL;
    }
    return TRUE;
}

BOOL
CompressAddFileToHandleA (
    IN      PCSTR FileName,
    IN      PCSTR StoredName,
    IN OUT  PCOMPRESS_HANDLEA CompressedHandle
    )
{
    HANDLE fileHandle = INVALID_HANDLE_VALUE;
    LONGLONG fileSize;
    DWORD bytesRead;
    DWORD bytesWritten;
    DWORD bytesComp;
    DWORD bytesUncomp;
    DWORD signature = COMPRESS_NEWFILE;
    DWORD fileNameSize;
    DWORD headerSize;
    USHORT compType = 0;
    USHORT compSize = 0;
    PCWSTR unicodeName = NULL;
    UINT savedIndex = 0;
    LARGE_INTEGER savedSize;
    BOOL result = FALSE;

    __try {

        // save the state of the compress handle
        savedIndex = CompressedHandle->CurrFileIndex;
        savedSize.QuadPart = CompressedHandle->CurrFileSize;

        fileSize = BfGetFileSizeA (FileName);
        fileHandle = BfOpenReadFileA (FileName);
        if ((fileHandle == NULL) ||
            (fileHandle == INVALID_HANDLE_VALUE)
            ) {
            __leave;
        }

        // handle UNICODE files
        unicodeName = ConvertAtoW (StoredName);
        if (!unicodeName) {
            __leave;
        }
        fileNameSize = SizeOfStringW (unicodeName);
        headerSize = sizeof (DWORD) + sizeof (LONGLONG) + sizeof (DWORD) + fileNameSize;

        if (CompressedHandle->CurrFileSize + headerSize > CompressedHandle->MaxFileSize) {
            if (!pPrepareNextFileA (CompressedHandle, FALSE)) {
                __leave;
            }
        }

        if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
            __leave;
        }
        CompressedHandle->CurrFileSize += sizeof (DWORD);

        if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&fileSize), sizeof (LONGLONG))) {
            __leave;
        }
        CompressedHandle->CurrFileSize += sizeof (LONGLONG);

        if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&fileNameSize), sizeof (DWORD))) {
            __leave;
        }
        CompressedHandle->CurrFileSize += sizeof (DWORD);

        if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(unicodeName), fileNameSize)) {
            __leave;
        }
        CompressedHandle->CurrFileSize += fileNameSize;
        FreeConvertedStr (unicodeName);
        unicodeName = NULL;

        while (fileSize) {

            ZeroMemory (CompressedHandle->ReadBuffer, COMPRESS_BUFFER_SIZE + 2 * sizeof (USHORT));
            ZeroMemory (CompressedHandle->CompBuffer, COMPRESS_BUFFER_SIZE + 2 * sizeof (USHORT));

            if (!ReadFile (fileHandle, CompressedHandle->ReadBuffer + 2 * sizeof (USHORT), COMPRESS_BUFFER_SIZE, &bytesRead, NULL)) {
                __leave;
            }
            if (bytesRead == 0)
            {
                // Somehow the file points is beyond the end of the file. Probably file in use.
                SetLastError(ERROR_SHARING_VIOLATION);
                __leave;
            }

            bytesComp = CompressData (
                            CompressMrci1,
                            CompressedHandle->ReadBuffer + 2 * sizeof (USHORT),
                            bytesRead,
                            CompressedHandle->CompBuffer + 2 * sizeof (USHORT),
                            COMPRESS_BUFFER_SIZE
                            );

            if (bytesComp < bytesRead) {
                bytesUncomp = DecompressData (
                                CompressMrci1,
                                CompressedHandle->CompBuffer + 2 * sizeof (USHORT),
                                bytesComp,
                                CompressedHandle->ExtraBuffer,
                                COMPRESS_BUFFER_SIZE
                                );
                if (bytesUncomp != bytesRead) {
                    bytesComp = COMPRESS_BUFFER_SIZE;
                } else {
                    if (!TestBuffer (CompressedHandle->ReadBuffer + 2 * sizeof (USHORT), CompressedHandle->ExtraBuffer, bytesRead)) {
                        bytesComp = COMPRESS_BUFFER_SIZE;
                    }
                }
            }

            if (bytesComp >= bytesRead) {
                compType = CompressNone;
                compSize = (USHORT)bytesRead;
                CopyMemory (CompressedHandle->ReadBuffer, &compType, sizeof (USHORT));
                CopyMemory (CompressedHandle->ReadBuffer + sizeof (USHORT), &compSize, sizeof (USHORT));

                compSize += (2 * sizeof (USHORT));

                if (CompressedHandle->CurrFileSize + compSize > CompressedHandle->MaxFileSize) {
                    if (!pPrepareNextFileA (CompressedHandle, FALSE)) {
                        __leave;
                    }
                }

                if (!BfWriteFile (CompressedHandle->CurrFileHandle, CompressedHandle->ReadBuffer, compSize)) {
                    __leave;
                }
                CompressedHandle->CurrFileSize += compSize;
            } else {
                compType = CompressMrci1;
                compSize = (USHORT)bytesComp;
                CopyMemory (CompressedHandle->CompBuffer, &compType, sizeof (USHORT));
                CopyMemory (CompressedHandle->CompBuffer + sizeof (USHORT), &compSize, sizeof (USHORT));

                compSize += (2 * sizeof (USHORT));

                if (CompressedHandle->CurrFileSize + compSize > CompressedHandle->MaxFileSize) {
                    if (!pPrepareNextFileA (CompressedHandle, FALSE)) {
                        __leave;
                    }
                }

                if (!BfWriteFile (CompressedHandle->CurrFileHandle, CompressedHandle->CompBuffer, compSize)) {
                    __leave;
                }
                CompressedHandle->CurrFileSize += compSize;
            }
            fileSize -= bytesRead;
        }
        CompressedHandle->FilesStored ++;

        result = TRUE;
    }
    __finally {
        PushError ();
        if (unicodeName) {
            FreeConvertedStr (unicodeName);
            unicodeName = NULL;
        }
        if (fileHandle != INVALID_HANDLE_VALUE) {
            CloseHandle (fileHandle);
        }
        if (!result) {
            // let's restore the state of the compress handle
            if (savedIndex == CompressedHandle->CurrFileIndex) {
                if (savedSize.QuadPart != CompressedHandle->CurrFileSize) {
                    SetFilePointer (CompressedHandle->CurrFileHandle, savedSize.LowPart, &(savedSize.HighPart), FILE_BEGIN);
                    SetEndOfFile (CompressedHandle->CurrFileHandle);
                    CompressedHandle->CurrFileSize = savedSize.QuadPart;
                }
            } else {
                CompressedHandle->CurrFileIndex = savedIndex - 1;
                pPrepareNextFileA (CompressedHandle, TRUE);
                SetFilePointer (CompressedHandle->CurrFileHandle, savedSize.LowPart, &(savedSize.HighPart), FILE_BEGIN);
                SetEndOfFile (CompressedHandle->CurrFileHandle);
                CompressedHandle->CurrFileSize = savedSize.QuadPart;
                pDeleteNextFilesA (CompressedHandle, savedIndex);
            }
        }
        PopError ();
    }

    return result;
}

BOOL
CompressAddFileToHandleW (
    IN      PCWSTR FileName,
    IN      PCWSTR StoredName,
    IN OUT  PCOMPRESS_HANDLEW CompressedHandle
    )
{
    HANDLE fileHandle = INVALID_HANDLE_VALUE;
    LONGLONG fileSize;
    DWORD bytesRead;
    DWORD bytesWritten;
    DWORD bytesComp;
    DWORD bytesUncomp;
    DWORD signature = COMPRESS_NEWFILE;
    DWORD fileNameSize;
    DWORD headerSize;
    USHORT compType = 0;
    USHORT compSize = 0;
    UINT savedIndex = 0;
    LARGE_INTEGER savedSize;
    BOOL result = FALSE;

    __try {

        // save the state of the compress handle
        savedIndex = CompressedHandle->CurrFileIndex;
        savedSize.QuadPart = CompressedHandle->CurrFileSize;

        fileSize = BfGetFileSizeW (FileName);
        fileHandle = BfOpenReadFileW (FileName);
        if ((fileHandle == NULL) ||
            (fileHandle == INVALID_HANDLE_VALUE)
            ) {
            __leave;
        }

        fileNameSize = SizeOfStringW (StoredName);
        headerSize = sizeof (DWORD) + sizeof (LONGLONG) + sizeof (DWORD) + fileNameSize;

        if (CompressedHandle->CurrFileSize + headerSize > CompressedHandle->MaxFileSize) {
            if (!pPrepareNextFileW (CompressedHandle, FALSE)) {
                __leave;
            }
        }

        if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
            __leave;
        }
        CompressedHandle->CurrFileSize += sizeof (DWORD);

        if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&fileSize), sizeof (LONGLONG))) {
            __leave;
        }
        CompressedHandle->CurrFileSize += sizeof (LONGLONG);

        if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&fileNameSize), sizeof (DWORD))) {
            __leave;
        }
        CompressedHandle->CurrFileSize += sizeof (DWORD);

        if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(StoredName), fileNameSize)) {
            __leave;
        }
        CompressedHandle->CurrFileSize += fileNameSize;

        while (fileSize) {

            ZeroMemory (CompressedHandle->ReadBuffer, COMPRESS_BUFFER_SIZE + 2 * sizeof (USHORT));
            ZeroMemory (CompressedHandle->CompBuffer, COMPRESS_BUFFER_SIZE + 2 * sizeof (USHORT));

            if (!ReadFile (fileHandle, CompressedHandle->ReadBuffer + 2 * sizeof (USHORT), COMPRESS_BUFFER_SIZE, &bytesRead, NULL)) {
                __leave;
            }
            if (bytesRead == 0)
            {
                // Somehow the file points is beyond the end of the file. Probably file in use.
                SetLastError(ERROR_SHARING_VIOLATION);
                __leave;
            }

            bytesComp = CompressData (
                            CompressMrci1,
                            CompressedHandle->ReadBuffer + 2 * sizeof (USHORT),
                            bytesRead,
                            CompressedHandle->CompBuffer + 2 * sizeof (USHORT),
                            COMPRESS_BUFFER_SIZE
                            );

            if (bytesComp < bytesRead) {
                bytesUncomp = DecompressData (
                                CompressMrci1,
                                CompressedHandle->CompBuffer + 2 * sizeof (USHORT),
                                bytesComp,
                                CompressedHandle->ExtraBuffer,
                                COMPRESS_BUFFER_SIZE
                                );
                if (bytesUncomp != bytesRead) {
                    bytesComp = COMPRESS_BUFFER_SIZE;
                } else {
                    if (!TestBuffer (CompressedHandle->ReadBuffer + 2 * sizeof (USHORT), CompressedHandle->ExtraBuffer, bytesRead)) {
                        bytesComp = COMPRESS_BUFFER_SIZE;
                    }
                }
            }

            if (bytesComp >= bytesRead) {
                compType = CompressNone;
                compSize = (USHORT)bytesRead;
                CopyMemory (CompressedHandle->ReadBuffer, &compType, sizeof (USHORT));
                CopyMemory (CompressedHandle->ReadBuffer + sizeof (USHORT), &compSize, sizeof (USHORT));

                compSize += (2 * sizeof (USHORT));

                if (CompressedHandle->CurrFileSize + compSize > CompressedHandle->MaxFileSize) {
                    if (!pPrepareNextFileW (CompressedHandle, FALSE)) {
                        __leave;
                    }
                }

                if (!BfWriteFile (CompressedHandle->CurrFileHandle, CompressedHandle->ReadBuffer, compSize)) {
                    __leave;
                }
                CompressedHandle->CurrFileSize += compSize;
            } else {
                compType = CompressMrci1;
                compSize = (USHORT)bytesComp;
                CopyMemory (CompressedHandle->CompBuffer, &compType, sizeof (USHORT));
                CopyMemory (CompressedHandle->CompBuffer + sizeof (USHORT), &compSize, sizeof (USHORT));

                compSize += (2 * sizeof (USHORT));

                if (CompressedHandle->CurrFileSize + compSize > CompressedHandle->MaxFileSize) {
                    if (!pPrepareNextFileW (CompressedHandle, FALSE)) {
                        __leave;
                    }
                }

                if (!BfWriteFile (CompressedHandle->CurrFileHandle, CompressedHandle->CompBuffer, compSize)) {
                    __leave;
                }
                CompressedHandle->CurrFileSize += compSize;
            }
            fileSize -= bytesRead;
        }
        CompressedHandle->FilesStored ++;

        result = TRUE;
    }
    __finally {
        PushError ();
        if (fileHandle != INVALID_HANDLE_VALUE) {
            CloseHandle (fileHandle);
        }
        if (!result) {
            // let's restore the state of the compress handle
            if (savedIndex == CompressedHandle->CurrFileIndex) {
                if (savedSize.QuadPart != CompressedHandle->CurrFileSize) {
                    SetFilePointer (CompressedHandle->CurrFileHandle, savedSize.LowPart, &(savedSize.HighPart), FILE_BEGIN);
                    SetEndOfFile (CompressedHandle->CurrFileHandle);
                    CompressedHandle->CurrFileSize = savedSize.QuadPart;
                }
            } else {
                CompressedHandle->CurrFileIndex = savedIndex - 1;
                pPrepareNextFileW (CompressedHandle, TRUE);
                SetFilePointer (CompressedHandle->CurrFileHandle, savedSize.LowPart, &(savedSize.HighPart), FILE_BEGIN);
                SetEndOfFile (CompressedHandle->CurrFileHandle);
                CompressedHandle->CurrFileSize = savedSize.QuadPart;
                pDeleteNextFilesW (CompressedHandle, savedIndex);
            }
        }
        PopError ();
    }

    return result;
}

BOOL
CompressFlushAndCloseHandleA (
    IN OUT  PCOMPRESS_HANDLEA CompressedHandle
    )
{
    CHAR currFile [MAX_PATH];
    PCSTR currFullPath = NULL;
    DWORD signature = COMPRESS_SIG;
    BOOL result = FALSE;

    __try {

        if ((CompressedHandle) &&
            (CompressedHandle->CurrFileHandle) &&
            (CompressedHandle->CurrFileHandle != INVALID_HANDLE_VALUE)
            ) {
            result = CloseHandle (CompressedHandle->CurrFileHandle);
            CompressedHandle->CurrFileHandle = NULL;
            if (result) {
                // write the total number of files compressed into the first file
                result = FALSE;
                wsprintfA (currFile, CompressedHandle->MainFilePattern, CompressedHandle->FirstFileIndex);
                currFullPath = JoinPathsA (CompressedHandle->StorePath, currFile);
                CompressedHandle->CurrFileHandle = BfOpenFileA (currFullPath);
                if ((CompressedHandle->CurrFileHandle == NULL) ||
                    (CompressedHandle->CurrFileHandle == INVALID_HANDLE_VALUE)
                    ) {
                    __leave;
                }
                FreePathStringA (currFullPath);
                currFullPath = NULL;

                // write again the signature
                if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
                    __leave;
                }

                // write number of files compressed
                if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&CompressedHandle->FilesStored), sizeof (LONGLONG))) {
                    __leave;
                }

                result = CloseHandle (CompressedHandle->CurrFileHandle);
                CompressedHandle->CurrFileHandle = NULL;
            }
        }
    }
    __finally {
        PushError ();
        if (currFullPath) {
            FreePathStringA (currFullPath);
            currFullPath = NULL;
        }
        CompressCleanupHandleA (CompressedHandle);
        PopError ();
    }

    return result;
}

BOOL
CompressFlushAndCloseHandleW (
    IN OUT  PCOMPRESS_HANDLEW CompressedHandle
    )
{
    WCHAR currFile [MAX_PATH];
    PCWSTR currFullPath = NULL;
    DWORD signature = COMPRESS_SIG;
    BOOL result = FALSE;

    __try {

        if ((CompressedHandle) &&
            (CompressedHandle->CurrFileHandle) &&
            (CompressedHandle->CurrFileHandle != INVALID_HANDLE_VALUE)
            ) {
            result = CloseHandle (CompressedHandle->CurrFileHandle);
            CompressedHandle->CurrFileHandle = NULL;
            if (result) {
                // write the total number of files compressed into the first file
                result = FALSE;
                wsprintfW (currFile, CompressedHandle->MainFilePattern, CompressedHandle->FirstFileIndex);
                currFullPath = JoinPathsW (CompressedHandle->StorePath, currFile);
                CompressedHandle->CurrFileHandle = BfOpenFileW (currFullPath);
                if ((CompressedHandle->CurrFileHandle == NULL) ||
                    (CompressedHandle->CurrFileHandle == INVALID_HANDLE_VALUE)
                    ) {
                    __leave;
                }
                FreePathStringW (currFullPath);
                currFullPath = NULL;

                // write again the signature
                if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
                    __leave;
                }

                // write number of files compressed
                if (!BfWriteFile (CompressedHandle->CurrFileHandle, (PBYTE)(&CompressedHandle->FilesStored), sizeof (LONGLONG))) {
                    __leave;
                }

                result = CloseHandle (CompressedHandle->CurrFileHandle);
                CompressedHandle->CurrFileHandle = NULL;
            }
        }
    }
    __finally {
        PushError ();
        if (currFullPath) {
            FreePathStringW (currFullPath);
            currFullPath = NULL;
        }
        CompressCleanupHandleW (CompressedHandle);
        PopError ();
    }

    return result;
}

BOOL
CompressOpenHandleA (
    IN      PCSTR StorePath,
    IN      PCSTR MainFilePattern,
    IN      UINT StartIndex,
    OUT     PCOMPRESS_HANDLEA CompressedHandle
    )
{
    CHAR currFile [MAX_PATH];
    PCSTR currFullPath = NULL;
    DWORD signature = 0;
    BOOL result = FALSE;

    __try {
        ZeroMemory (CompressedHandle, sizeof (COMPRESS_HANDLEA));
        if (StartIndex == 0) {
            CompressedHandle->CurrFileIndex = 1;
        } else {
            CompressedHandle->CurrFileIndex = StartIndex;
        }
        CompressedHandle->FirstFileIndex = CompressedHandle->CurrFileIndex;
        CompressedHandle->StorePath = DuplicatePathStringA (StorePath, 0);
        if (!CompressedHandle->StorePath) {
            __leave;
        }
        CompressedHandle->MainFilePattern = DuplicatePathStringA (MainFilePattern, 0);
        if (!CompressedHandle->MainFilePattern) {
            __leave;
        }
        wsprintfA (currFile, CompressedHandle->MainFilePattern, CompressedHandle->CurrFileIndex);
        currFullPath = JoinPathsA (CompressedHandle->StorePath, currFile);
        CompressedHandle->CurrFileHandle = BfOpenReadFileA (currFullPath);
        if ((CompressedHandle->CurrFileHandle == NULL) ||
            (CompressedHandle->CurrFileHandle == INVALID_HANDLE_VALUE)
            ) {
            __leave;
        }
        FreePathStringA (currFullPath);
        currFullPath = NULL;

        if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
            __leave;
        }
        CompressedHandle->CurrFileSize += sizeof (DWORD);

        if (signature != COMPRESS_SIG) {
            SetLastError (ERROR_INVALID_DATA);
            __leave;
        }

        if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&CompressedHandle->FilesStored), sizeof (LONGLONG))) {
            __leave;
        }
        CompressedHandle->CurrFileSize += sizeof (LONGLONG);

        CompressedHandle->ReadBuffer = MemAlloc (g_hHeap, 0, COMPRESS_BUFFER_SIZE + 2 * sizeof (USHORT));
        if (!CompressedHandle->ReadBuffer) {
            __leave;
        }

        CompressedHandle->CompBuffer = MemAlloc (g_hHeap, 0, COMPRESS_BUFFER_SIZE + 2 * sizeof (USHORT));
        if (!CompressedHandle->CompBuffer) {
            __leave;
        }

        result = TRUE;
    }
    __finally {
        PushError ();
        if (currFullPath) {
            FreePathStringA (currFullPath);
            currFullPath = NULL;
        }
        if (!result) {
            CompressCleanupHandleA (CompressedHandle);
        }
        PopError ();
    }
    return result;
}

BOOL
CompressOpenHandleW (
    IN      PCWSTR StorePath,
    IN      PCWSTR MainFilePattern,
    IN      UINT StartIndex,
    OUT     PCOMPRESS_HANDLEW CompressedHandle
    )
{
    WCHAR currFile [MAX_PATH];
    PCWSTR currFullPath = NULL;
    DWORD signature = 0;
    BOOL result = FALSE;

    __try {
        ZeroMemory (CompressedHandle, sizeof (COMPRESS_HANDLEW));
        if (StartIndex == 0) {
            CompressedHandle->CurrFileIndex = 1;
        } else {
            CompressedHandle->CurrFileIndex = StartIndex;
        }
        CompressedHandle->FirstFileIndex = CompressedHandle->CurrFileIndex;
        CompressedHandle->StorePath = DuplicatePathStringW (StorePath, 0);
        if (!CompressedHandle->StorePath) {
            __leave;
        }
        CompressedHandle->MainFilePattern = DuplicatePathStringW (MainFilePattern, 0);
        if (!CompressedHandle->MainFilePattern) {
            __leave;
        }
        wsprintfW (currFile, CompressedHandle->MainFilePattern, CompressedHandle->CurrFileIndex);
        currFullPath = JoinPathsW (CompressedHandle->StorePath, currFile);
        CompressedHandle->CurrFileHandle = BfOpenReadFileW (currFullPath);
        if ((CompressedHandle->CurrFileHandle == NULL) ||
            (CompressedHandle->CurrFileHandle == INVALID_HANDLE_VALUE)
            ) {
            __leave;
        }
        FreePathStringW (currFullPath);
        currFullPath = NULL;

        if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
            __leave;
        }
        CompressedHandle->CurrFileSize += sizeof (DWORD);

        if (signature != COMPRESS_SIG) {
            SetLastError (ERROR_INVALID_DATA);
            __leave;
        }

        if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&CompressedHandle->FilesStored), sizeof (LONGLONG))) {
            __leave;
        }
        CompressedHandle->CurrFileSize += sizeof (LONGLONG);

        CompressedHandle->ReadBuffer = MemAlloc (g_hHeap, 0, COMPRESS_BUFFER_SIZE + 2 * sizeof (USHORT));
        if (!CompressedHandle->ReadBuffer) {
            __leave;
        }

        CompressedHandle->CompBuffer = MemAlloc (g_hHeap, 0, COMPRESS_BUFFER_SIZE + 2 * sizeof (USHORT));
        if (!CompressedHandle->CompBuffer) {
            __leave;
        }

        result = TRUE;
    }
    __finally {
        PushError ();
        if (currFullPath) {
            FreePathStringW (currFullPath);
            currFullPath = NULL;
        }
        if (!result) {
            CompressCleanupHandleW (CompressedHandle);
        }
        PopError ();
    }
    return result;
}

BOOL
CompressExtractAllFilesA (
    IN      PCSTR ExtractPath,
    IN OUT  PCOMPRESS_HANDLEA CompressedHandle,
    IN      PCOMPRESSNOTIFICATIONA CompressNotification OPTIONAL
    )
{
    DWORD signature;
    LONGLONG fileSize;
    LONGLONG fileSizeRead;
    DWORD fileNameSize;
    PCWSTR storedName = NULL;
    PCSTR storedNameA = NULL;
    PCSTR extractPath = NULL;
    PCSTR newFileName = NULL;
    BOOL extractFile = TRUE;
    HANDLE extractHandle = NULL;
    USHORT compType = 0;
    USHORT compSize = 0;
    DWORD bytesComp;
    LARGE_INTEGER savedSize;
    BOOL result = FALSE;

    __try {

        for (;;) {

            // read the header for this file

            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
                // It is possible that we continue onto the next file, let's try that.
                if (!pPrepareNextFileA (CompressedHandle, TRUE)) {
                    result = TRUE;
                    __leave;
                }
                if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
                    __leave;
                }
            }
            CompressedHandle->CurrFileSize += sizeof (DWORD);
            if (signature != COMPRESS_NEWFILE) {
                SetLastError (ERROR_INVALID_DATA);
                __leave;
            }

            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&fileSize), sizeof (LONGLONG))) {
                __leave;
            }
            CompressedHandle->CurrFileSize += sizeof (LONGLONG);
            fileSizeRead = 0;

            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&fileNameSize), sizeof (DWORD))) {
                __leave;
            }
            CompressedHandle->CurrFileSize += sizeof (DWORD);

            storedName = MemAlloc (g_hHeap, 0, fileNameSize);

            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(storedName), fileNameSize)) {
                __leave;
            }
            CompressedHandle->CurrFileSize += fileNameSize;

            storedNameA = ConvertWtoA (storedName);
            if (!storedNameA) {
                SetLastError (ERROR_NOT_ENOUGH_MEMORY);
                __leave;
            }

            extractPath = JoinPathsA (ExtractPath, storedNameA);
            if (!extractPath) {
                SetLastError (ERROR_NOT_ENOUGH_MEMORY);
                __leave;
            }

            extractFile = TRUE;
            newFileName = NULL;
            if (CompressNotification) {
                if (!CompressNotification (extractPath, fileSize, &extractFile, &newFileName)) {
                    __leave;
                }
            }

            if (extractFile) {
                if (newFileName) {
                    // let's make sure that the directory exists
                    BfCreateDirectoryExA (newFileName, FALSE);
                }
                extractHandle = BfCreateFileA (newFileName?newFileName:extractPath);
                if ((extractHandle == NULL) ||
                    (extractHandle == INVALID_HANDLE_VALUE)
                    ) {
                    __leave;
                }
            } else {
                extractHandle = NULL;
            }

            if (newFileName) {
                FreePathStringA (newFileName);
                newFileName = NULL;
            }

            FreePathStringA (extractPath);
            extractPath = NULL;

            MemFree (g_hHeap, 0, storedName);
            storedName = NULL;

            FreeConvertedStr (storedNameA);
            storedNameA = NULL;

            if (fileSize > 0) {
                if (!extractFile && g_ErrorMode) {
                    for (;;) {
                        if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&compType), sizeof (USHORT))) {
                            // It is possible that we continue onto the next file, let's try that.
                            if (!pPrepareNextFileA (CompressedHandle, TRUE)) {
                                // we might be at the end of the compressed file, there are no other files here
                                result = TRUE;
                                __leave;
                            }
                            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&compType), sizeof (USHORT))) {
                                __leave;
                            }
                        }
                        CompressedHandle->CurrFileSize += sizeof (USHORT);

                        if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&compSize), sizeof (USHORT))) {
                            __leave;
                        }
                        CompressedHandle->CurrFileSize += sizeof (USHORT);

                        // let's try to see if we just read a new file signature
                        *((PUSHORT)(&signature) + 0) = compType;
                        *((PUSHORT)(&signature) + 1) = compSize;
                        if (signature == COMPRESS_NEWFILE) {
                            // this is a new file
                            CompressedHandle->CurrFileSize -= sizeof (USHORT);
                            CompressedHandle->CurrFileSize -= sizeof (USHORT);
                            // rewind the file current pointer;
                            savedSize.QuadPart = CompressedHandle->CurrFileSize;
                            SetFilePointer (CompressedHandle->CurrFileHandle, savedSize.LowPart, &(savedSize.HighPart), FILE_BEGIN);
                            // we are done with the current file
                            break;
                        } else {
                            // Let's advance the file pointer
                            if (SetFilePointer (CompressedHandle->CurrFileHandle, compSize, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER) {
                                __leave;
                            }
                            CompressedHandle->CurrFileSize += compSize;
                        }
                    }
                } else {
                    for (;;) {
                        if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&compType), sizeof (USHORT))) {
                            // It is possible that we continue onto the next file, let's try that.
                            if (!pPrepareNextFileA (CompressedHandle, TRUE)) {
                                __leave;
                            }
                            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&compType), sizeof (USHORT))) {
                                __leave;
                            }
                        }
                        CompressedHandle->CurrFileSize += sizeof (USHORT);

                        if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&compSize), sizeof (USHORT))) {
                            __leave;
                        }
                        CompressedHandle->CurrFileSize += sizeof (USHORT);

                        if (!BfReadFile (CompressedHandle->CurrFileHandle, CompressedHandle->CompBuffer, compSize)) {
                            __leave;
                        }
                        CompressedHandle->CurrFileSize += compSize;

                        if (compType == CompressNone) {
                            if (extractFile) {
                                if (!BfWriteFile (extractHandle, CompressedHandle->CompBuffer, compSize)) {
                                    __leave;
                                }
                            }
                            fileSizeRead += compSize;
                        } else {
                            bytesComp = DecompressData (
                                            compType,
                                            CompressedHandle->CompBuffer,
                                            compSize,
                                            CompressedHandle->ReadBuffer,
                                            COMPRESS_BUFFER_SIZE
                                            );
                            if (bytesComp > COMPRESS_BUFFER_SIZE) {
                                SetLastError (ERROR_INVALID_DATA);
                                __leave;
                            }
                            if (extractFile) {
                                if (!BfWriteFile (extractHandle, CompressedHandle->ReadBuffer, bytesComp)) {
                                    __leave;
                                }
                            }
                            fileSizeRead += bytesComp;
                        }

                        if (fileSizeRead == fileSize) {
                            // this file is done, let's go to the next one
                            break;
                        }
                    }
                }
            }

            if (extractHandle) {
                CloseHandle (extractHandle);
                extractHandle = NULL;
            }
        }
    }
    __finally {
        if (storedName != NULL) {
            MemFree (g_hHeap, 0, storedName);
            storedName = NULL;
        }
        if (storedNameA != NULL) {
            FreeConvertedStr (storedNameA);
            storedNameA = NULL;
        }
        if (newFileName != NULL) {
            FreePathStringA (newFileName);
            newFileName = NULL;
        }
        if (extractPath != NULL) {
            FreePathStringA (extractPath);
            extractPath = NULL;
        }
        if ((extractHandle != NULL) &&
            (extractHandle != INVALID_HANDLE_VALUE)
            ) {
            CloseHandle (extractHandle);
            extractHandle = NULL;
        }
    }

    return result;
}

BOOL
CompressExtractAllFilesW (
    IN      PCWSTR ExtractPath,
    IN OUT  PCOMPRESS_HANDLEW CompressedHandle,
    IN      PCOMPRESSNOTIFICATIONW CompressNotification OPTIONAL
    )
{
    DWORD signature;
    LONGLONG fileSize;
    LONGLONG fileSizeRead;
    DWORD fileNameSize;
    PCWSTR storedName = NULL;
    PCWSTR extractPath = NULL;
    PCWSTR newFileName = NULL;
    BOOL extractFile = TRUE;
    HANDLE extractHandle = NULL;
    USHORT compType = 0;
    USHORT compSize = 0;
    DWORD bytesComp;
    LARGE_INTEGER savedSize;
    BOOL result = FALSE;

    __try {

        for (;;) {

            // read the header for this file

            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
                // It is possible that we continue onto the next file, let's try that.
                if (!pPrepareNextFileW (CompressedHandle, TRUE)) {
                    result = TRUE;
                    __leave;
                }
                if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&signature), sizeof (DWORD))) {
                    __leave;
                }
            }
            CompressedHandle->CurrFileSize += sizeof (DWORD);
            if (signature != COMPRESS_NEWFILE) {
                SetLastError (ERROR_INVALID_DATA);
                __leave;
            }

            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&fileSize), sizeof (LONGLONG))) {
                __leave;
            }
            CompressedHandle->CurrFileSize += sizeof (LONGLONG);
            fileSizeRead = 0;

            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&fileNameSize), sizeof (DWORD))) {
                __leave;
            }
            CompressedHandle->CurrFileSize += sizeof (DWORD);

            storedName = MemAlloc (g_hHeap, 0, fileNameSize);

            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(storedName), fileNameSize)) {
                __leave;
            }
            CompressedHandle->CurrFileSize += fileNameSize;

            extractPath = JoinPathsW (ExtractPath, storedName);
            if (!extractPath) {
                SetLastError (ERROR_NOT_ENOUGH_MEMORY);
                __leave;
            }

            extractFile = TRUE;
            newFileName = NULL;
            if (CompressNotification) {
                if (!CompressNotification (extractPath, fileSize, &extractFile, &newFileName)) {
                    __leave;
                }
            }

            if (extractFile) {
                if (newFileName) {
                    // let's make sure that the directory exists
                    BfCreateDirectoryExW (newFileName, FALSE);
                }
                extractHandle = BfCreateFileW (newFileName?newFileName:extractPath);
                if ((extractHandle == NULL) ||
                    (extractHandle == INVALID_HANDLE_VALUE)
                    ) {
                    __leave;
                }
            } else {
                extractHandle = NULL;
            }

            if (newFileName) {
                FreePathStringW (newFileName);
                newFileName = NULL;
            }

            FreePathStringW (extractPath);
            extractPath = NULL;

            MemFree (g_hHeap, 0, storedName);
            storedName = NULL;

            if (fileSize) {
                if (!extractFile && g_ErrorMode) {
                    for (;;) {
                        if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&compType), sizeof (USHORT))) {
                            // It is possible that we continue onto the next file, let's try that.
                            if (!pPrepareNextFileW (CompressedHandle, TRUE)) {
                                // we might be at the end of the compressed file, there are no other files here
                                result = TRUE;
                                __leave;
                            }
                            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&compType), sizeof (USHORT))) {
                                __leave;
                            }
                        }
                        CompressedHandle->CurrFileSize += sizeof (USHORT);

                        if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&compSize), sizeof (USHORT))) {
                            __leave;
                        }
                        CompressedHandle->CurrFileSize += sizeof (USHORT);

                        // let's try to see if we just read a new file signature
                        *((PUSHORT)(&signature + 0)) = compType;
                        *((PUSHORT)(&signature + 1)) = compSize;
                        if (signature == COMPRESS_NEWFILE) {
                            // this is a new file
                            CompressedHandle->CurrFileSize -= sizeof (USHORT);
                            CompressedHandle->CurrFileSize -= sizeof (USHORT);
                            // rewind the file current pointer;
                            savedSize.QuadPart = CompressedHandle->CurrFileSize;
                            SetFilePointer (CompressedHandle->CurrFileHandle, savedSize.LowPart, &(savedSize.HighPart), FILE_BEGIN);
                            // we are done with the current file
                            break;
                        } else {
                            // Let's advance the file pointer
                            if (SetFilePointer (CompressedHandle->CurrFileHandle, compSize, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER) {
                                __leave;
                            }
                            CompressedHandle->CurrFileSize += compSize;
                        }
                    }
                } else {
                    for (;;) {
                        if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&compType), sizeof (USHORT))) {
                            // It is possible that we continue onto the next file, let's try that.
                            if (!pPrepareNextFileW (CompressedHandle, TRUE)) {
                                __leave;
                            }
                            if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&compType), sizeof (USHORT))) {
                                __leave;
                            }
                        }
                        CompressedHandle->CurrFileSize += sizeof (USHORT);

                        if (!BfReadFile (CompressedHandle->CurrFileHandle, (PBYTE)(&compSize), sizeof (USHORT))) {
                            __leave;
                        }
                        CompressedHandle->CurrFileSize += sizeof (USHORT);

                        if (!BfReadFile (CompressedHandle->CurrFileHandle, CompressedHandle->CompBuffer, compSize)) {
                            __leave;
                        }
                        CompressedHandle->CurrFileSize += compSize;

                        if (compType == CompressNone) {
                            if (extractFile) {
                                if (!BfWriteFile (extractHandle, CompressedHandle->CompBuffer, compSize)) {
                                    __leave;
                                }
                            }
                            fileSizeRead += compSize;
                        } else {
                            bytesComp = DecompressData (
                                            compType,
                                            CompressedHandle->CompBuffer,
                                            compSize,
                                            CompressedHandle->ReadBuffer,
                                            COMPRESS_BUFFER_SIZE
                                            );
                            if (bytesComp > COMPRESS_BUFFER_SIZE) {
                                SetLastError (ERROR_INVALID_DATA);
                                __leave;
                            }
                            if (extractFile) {
                                if (!BfWriteFile (extractHandle, CompressedHandle->ReadBuffer, bytesComp)) {
                                    __leave;
                                }
                            }
                            fileSizeRead += bytesComp;
                        }

                        if (fileSizeRead == fileSize) {
                            // this file is done, let's go to the next one
                            break;
                        }
                    }
                }
            }

            if (extractHandle) {
                CloseHandle (extractHandle);
                extractHandle = NULL;
            }
        }
    }
    __finally {
        if (storedName != NULL) {
            MemFree (g_hHeap, 0, storedName);
            storedName = NULL;
        }
        if (newFileName != NULL) {
            FreePathStringW (newFileName);
            newFileName = NULL;
        }
        if (extractPath != NULL) {
            FreePathStringW (extractPath);
            extractPath = NULL;
        }
        if ((extractHandle != NULL) &&
            (extractHandle != INVALID_HANDLE_VALUE)
            ) {
            CloseHandle (extractHandle);
            extractHandle = NULL;
        }
    }

    return result;
}

BOOL
CompressSetErrorMode (
    IN      BOOL ErrorMode
    )
{
    BOOL oldErrorMode = g_ErrorMode;

    g_ErrorMode = ErrorMode;
    return oldErrorMode;
}