/*++

Copyright (c) 1999 Microsoft Corporation

Module Name:

    cablib.c

Abstract:

    Implements wrappers for cabinet APIs

Author:

    Calin Negreanu (calinn) 27-Apr-2000

Revision History:

    <alias> <date> <comments>

--*/

#include "pch.h"
#include <cablib.h>
#include <fci.h>
#include <fdi.h>
#include <fcntl.h>
#include <crt\sys\stat.h>

//
// Includes
//

// None

#define DBG_CABLIB  "CabLib"

//
// Strings
//

// None

//
// Constants
//

// None

//
// Macros
//

// None

//
// Types
//

typedef struct {
    PCSTR CabPath;
    PCSTR CabFileFormat;
    PCSTR CabDiskFormat;
    PCABGETCABINETNAMESA CabGetCabinetNames;
    PCABGETTEMPFILEA CabGetTempFile;
    HFCI FciHandle;
    ERF FciErrorStruct;
    CCAB FciCabParams;
    UINT FileCount;
    UINT CabCount;
    LONGLONG FileSize;
    LONGLONG CompressedSize;
} FCI_CAB_HANDLEA, *PFCI_CAB_HANDLEA;

typedef struct {
    PCWSTR CabPath;
    PCWSTR CabFileFormat;
    PCWSTR CabDiskFormat;
    PCABGETCABINETNAMESW CabGetCabinetNames;
    PCABGETTEMPFILEW CabGetTempFile;
    HFCI FciHandle;
    ERF FciErrorStruct;
    CCAB FciCabParams;
    UINT FileCount;
    UINT CabCount;
    LONGLONG FileSize;
    LONGLONG CompressedSize;
} FCI_CAB_HANDLEW, *PFCI_CAB_HANDLEW;

typedef struct {
    PCSTR CabPath;
    PCSTR CabFile;
    HFDI FdiHandle;
    ERF FdiErrorStruct;
    FDICABINETINFO FdiCabinetInfo;
} FDI_CAB_HANDLEA, *PFDI_CAB_HANDLEA;

typedef struct {
    PCWSTR CabPath;
    PCWSTR CabFile;
    HFDI FdiHandle;
    ERF FdiErrorStruct;
    FDICABINETINFO FdiCabinetInfo;
} FDI_CAB_HANDLEW, *PFDI_CAB_HANDLEW;

typedef struct {
    PCSTR ExtractPath;
    PCABNOTIFICATIONA CabNotification;
} CAB_DATAA, *PCAB_DATAA;

typedef struct {
    PCWSTR ExtractPath;
    PCABNOTIFICATIONW CabNotification;
} CAB_DATAW, *PCAB_DATAW;

//
// Globals
//

HASHTABLE g_UnicodeTable = NULL;

//
// Macro expansion list
//

// None

//
// Private function prototypes
//

// None

//
// Macro expansion definition
//

// None

//
// Code
//

INT
DIAMONDAPI
pCabFilePlacedA (
    IN      PCCAB FciCabParams,
    IN      PSTR FileName,
    IN      LONG FileSize,
    IN      BOOL Continuation,
    IN      PVOID Context
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    PFCI_CAB_HANDLEA cabHandle;

    cabHandle = (PFCI_CAB_HANDLEA) Context;
    if (!cabHandle) {
        return 0;
    }

    cabHandle->FileCount++;
    cabHandle->FileSize += FileSize;

    return 0;
}


INT
DIAMONDAPI
pCabFilePlacedW (
    IN      PCCAB FciCabParams,
    IN      PSTR FileName,
    IN      LONG FileSize,
    IN      BOOL Continuation,
    IN      PVOID Context
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    PFCI_CAB_HANDLEW cabHandle;

    cabHandle = (PFCI_CAB_HANDLEW) Context;
    if (!cabHandle) {
        return 0;
    }

    cabHandle->FileCount++;
    cabHandle->FileSize += FileSize;

    return 0;
}


PVOID
DIAMONDAPI
pCabAlloc (
    IN      ULONG Size
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    return MemAlloc (g_hHeap, 0, Size);
}

VOID
DIAMONDAPI
pCabFree (
    IN      PVOID Memory
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    if (Memory) {
        MemFree (g_hHeap, 0, Memory);
    }
}

INT_PTR
DIAMONDAPI
pCabOpenA (
    IN      PSTR FileName,
    IN      INT oFlag,
    IN      INT pMode,
    OUT     PINT Error,
    IN      PVOID Context
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    HANDLE fileHandle;

    // oFlag and pMode are prepared for using _open. We won't do that
    // and it's a terrible waste of time to check each individual flags
    // We'll just assert these values.
    MYASSERT ((oFlag == (_O_CREAT | _O_TRUNC | _O_BINARY | _O_RDWR)) || (oFlag == (_O_CREAT | _O_EXCL | _O_BINARY | _O_RDWR)));
    MYASSERT (pMode == (_S_IREAD | _S_IWRITE));

    fileHandle = CreateFileA (
                    FileName,
                    GENERIC_READ | GENERIC_WRITE,
                    0,
                    NULL,
                    CREATE_ALWAYS,
                    FILE_ATTRIBUTE_ARCHIVE,
                    NULL
                    );
    if (fileHandle == INVALID_HANDLE_VALUE) {
        *Error = GetLastError ();
        return -1;
    }
    *Error = 0;
    return (INT_PTR)fileHandle;
}

INT_PTR
DIAMONDAPI
pCabOpen1A (
    IN      PSTR FileName,
    IN      INT oFlag,
    IN      INT pMode
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    HANDLE fileHandle;

    // oFlag and pMode are prepared for using _open. We won't do that
    // and it's a terrible waste of time to check each individual flags
    // We'll just assert these values.
    MYASSERT (oFlag == _O_BINARY);

    fileHandle = CreateFileA (
                    FileName,
                    GENERIC_READ,
                    FILE_SHARE_READ,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_ARCHIVE,
                    NULL
                    );
    if (fileHandle == INVALID_HANDLE_VALUE) {
        return -1;
    }
    return (INT_PTR)fileHandle;
}

INT_PTR
DIAMONDAPI
pCabOpen1W (
    IN      PSTR FileName,
    IN      INT oFlag,
    IN      INT pMode
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    PFDI_CAB_HANDLEW cabHandle;
    HANDLE fileHandle = INVALID_HANDLE_VALUE;
    PCSTR fileName = NULL;
    PSTR fileNamePtr = NULL;
    PCWSTR fileNameW = NULL;
    PCWSTR fullFileName = NULL;

    // oFlag and pMode are prepared for using _open. We won't do that
    // and it's a terrible waste of time to check each individual flags
    // We'll just assert these values.
    MYASSERT (oFlag == _O_BINARY);

    if (g_UnicodeTable) {
        fileName = DuplicatePathStringA (FileName, 0);
        if (fileName) {
            fileNamePtr = (PSTR)GetFileNameFromPathA (fileName);
            if (fileNamePtr) {
                *fileNamePtr = 0;
                if ((HtFindStringAndDataA (g_UnicodeTable, fileName, &cabHandle) != NULL) &&
                    (cabHandle != NULL)
                    ) {
                    fileNamePtr = (PSTR)GetFileNameFromPathA (FileName);
                    if (fileNamePtr) {
                        fileNameW = ConvertAtoW (fileNamePtr);
                        if (fileNameW) {
                            fullFileName = JoinPathsW (cabHandle->CabPath, fileNameW);
                            if (fullFileName) {
                                fileHandle = CreateFileW (
                                                fullFileName,
                                                GENERIC_READ,
                                                FILE_SHARE_READ,
                                                NULL,
                                                OPEN_EXISTING,
                                                FILE_ATTRIBUTE_ARCHIVE,
                                                NULL
                                                );
                                FreePathStringW (fullFileName);
                                fullFileName = NULL;
                            }
                            FreeConvertedStr (fileNameW);
                            fileNameW = NULL;
                        }
                    }
                }
            }
            FreePathStringA (fileName);
            fileName = NULL;
        }
    }

    if (fileHandle == INVALID_HANDLE_VALUE) {
        fileHandle = CreateFileA (
                        FileName,
                        GENERIC_READ,
                        FILE_SHARE_READ,
                        NULL,
                        OPEN_EXISTING,
                        FILE_ATTRIBUTE_ARCHIVE,
                        NULL
                        );
    }

    if (fileHandle == INVALID_HANDLE_VALUE) {
        return -1;
    }

    return (INT_PTR)fileHandle;
}

UINT
DIAMONDAPI
pCabRead (
    IN      INT_PTR FileHandle,
    IN      PVOID Buffer,
    IN      UINT Size,
    OUT     PINT Error,
    IN      PVOID Context
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    BOOL result;
    UINT bytesRead;

    result = ReadFile ((HANDLE)FileHandle, Buffer, Size, &bytesRead, NULL);
    if (!result) {
        *Error = GetLastError ();
        return ((UINT)(-1));
    }
    *Error = 0;
    return bytesRead;
}

UINT
DIAMONDAPI
pCabRead1 (
    IN      INT_PTR FileHandle,
    IN      PVOID Buffer,
    IN      UINT Size
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    BOOL result;
    UINT bytesRead;

    result = ReadFile ((HANDLE)FileHandle, Buffer, Size, &bytesRead, NULL);
    if (!result) {
        return ((UINT)(-1));
    }
    return bytesRead;
}

UINT
DIAMONDAPI
pCabWrite (
    IN      INT_PTR FileHandle,
    IN      PVOID Buffer,
    IN      UINT Size,
    OUT     PINT Error,
    IN      PVOID Context
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    BOOL result;

    result = BfWriteFile ((HANDLE)FileHandle, Buffer, Size);
    if (!result) {
        *Error = GetLastError ();
        return ((UINT)(-1));
    }
    *Error = 0;
    return Size;
}

UINT
DIAMONDAPI
pCabWrite1 (
    IN      INT_PTR FileHandle,
    IN      PVOID Buffer,
    IN      UINT Size
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    BOOL result;

    result = BfWriteFile ((HANDLE)FileHandle, Buffer, Size);
    if (!result) {
        return ((UINT)(-1));
    }
    return Size;
}

INT
DIAMONDAPI
pCabClose (
    IN      INT_PTR FileHandle,
    OUT     PINT Error,
    IN      PVOID Context
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    CloseHandle ((HANDLE)FileHandle);
    *Error = 0;
    return 0;
}

INT
DIAMONDAPI
pCabClose1 (
    IN      INT_PTR FileHandle
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    CloseHandle ((HANDLE)FileHandle);
    return 0;
}

LONG
DIAMONDAPI
pCabSeek (
    IN      INT_PTR FileHandle,
    IN      LONG Distance,
    IN      INT SeekType,
    OUT     PINT Error,
    IN      PVOID Context
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    DWORD result;
    DWORD seekType = FILE_BEGIN;

    switch (SeekType) {
    case SEEK_SET:
        seekType = FILE_BEGIN;
        break;
    case SEEK_CUR:
        seekType = FILE_CURRENT;
        break;
    case SEEK_END:
        seekType = FILE_END;
        break;
    }

    result = SetFilePointer ((HANDLE)FileHandle, Distance, NULL, seekType);

    if (result == INVALID_SET_FILE_POINTER) {
        *Error = GetLastError ();
        return -1;
    }
    *Error = 0;
    return ((LONG)(result));
}

LONG
DIAMONDAPI
pCabSeek1 (
    IN      INT_PTR FileHandle,
    IN      LONG Distance,
    IN      INT SeekType
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    DWORD result;
    DWORD seekType = FILE_BEGIN;

    switch (SeekType) {
    case SEEK_SET:
        seekType = FILE_BEGIN;
        break;
    case SEEK_CUR:
        seekType = FILE_CURRENT;
        break;
    case SEEK_END:
        seekType = FILE_END;
        break;
    }

    result = SetFilePointer ((HANDLE)FileHandle, Distance, NULL, seekType);

    if (result == INVALID_SET_FILE_POINTER) {
        return -1;
    }
    return ((LONG)(result));
}

INT
DIAMONDAPI
pCabDeleteA (
    IN      PSTR FileName,
    OUT     PINT Error,
    IN      PVOID Context
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    if (!DeleteFileA (FileName)) {
        *Error = GetLastError ();
        return -1;
    }
    *Error = 0;
    return 0;
}

BOOL
DIAMONDAPI
pCabGetTempFileA (
    OUT     PSTR FileName,
    IN      INT FileNameLen,
    IN      PVOID Context
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    PFCI_CAB_HANDLEA cabHandle;

    cabHandle = (PFCI_CAB_HANDLEA) Context;

    if (cabHandle && cabHandle->CabGetTempFile) {
        return cabHandle->CabGetTempFile (FileName, FileNameLen);
    }

    return BfGetTempFileNameA (FileName, FileNameLen);
}

BOOL
DIAMONDAPI
pCabGetTempFileW (
    OUT     PSTR FileName,
    IN      INT FileNameLen,
    IN      PVOID Context
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    PFCI_CAB_HANDLEW cabHandle;
    WCHAR tempFileNameW [MAX_PATH];
    PCSTR tempFileNameA = NULL;

    cabHandle = (PFCI_CAB_HANDLEW) Context;

    if (cabHandle && cabHandle->CabGetTempFile) {
        if (cabHandle->CabGetTempFile (tempFileNameW, MAX_PATH)) {
            tempFileNameA = ConvertWtoA (tempFileNameW);
            if (tempFileNameA) {
                StringCopyTcharCountA (FileName, tempFileNameA, FileNameLen);
                FreeConvertedStr (tempFileNameA);
                return TRUE;
            }
        }
        return FALSE;
    }

    return BfGetTempFileNameA (FileName, FileNameLen);
}

BOOL
DIAMONDAPI
pCabGetNextCabinetA (
     IN     PCCAB FciCabParams,
     IN     ULONG PrevCabinetSize,
     IN     PVOID Context
     )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    PFCI_CAB_HANDLEA cabHandle;
    CHAR cabFile [1024];
    CHAR cabDisk [1024];

    cabHandle = (PFCI_CAB_HANDLEA) Context;
    if (!cabHandle) {
        return FALSE;
    }
    if (cabHandle->CabGetCabinetNames) {
        return cabHandle->CabGetCabinetNames (
                            FciCabParams->szCabPath,
                            CB_MAX_CAB_PATH,
                            FciCabParams->szCab,
                            CB_MAX_CABINET_NAME,
                            FciCabParams->szDisk,
                            CB_MAX_DISK_NAME,
                            FciCabParams->iCab,
                            &FciCabParams->iDisk
                            );
    } else {
        FciCabParams->iDisk = FciCabParams->iCab;
        if (cabHandle->CabFileFormat) {
            wsprintfA (cabFile, cabHandle->CabFileFormat, FciCabParams->iCab);
            StringCopyByteCountA (FciCabParams->szCab, cabFile, CB_MAX_CABINET_NAME * sizeof (CHAR));
        }
        if (cabHandle->CabDiskFormat) {
            wsprintfA (cabDisk, cabHandle->CabDiskFormat, FciCabParams->iDisk);
            StringCopyByteCountA (FciCabParams->szDisk, cabDisk, CB_MAX_DISK_NAME * sizeof (CHAR));
        }
    }
    return TRUE;
}

BOOL
DIAMONDAPI
pCabGetNextCabinetW (
     IN     PCCAB FciCabParams,
     IN     ULONG PrevCabinetSize,
     IN     PVOID Context
     )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    PFCI_CAB_HANDLEW cabHandle;
    WCHAR cabPath [1024];
    WCHAR cabFile [1024];
    WCHAR cabDisk [1024];
    BOOL result;

    cabHandle = (PFCI_CAB_HANDLEW) Context;
    if (!cabHandle) {
        return FALSE;
    }
    if (cabHandle->CabGetCabinetNames) {
        result = cabHandle->CabGetCabinetNames (
                            cabPath,
                            CB_MAX_CAB_PATH,
                            cabFile,
                            CB_MAX_CABINET_NAME,
                            cabDisk,
                            CB_MAX_DISK_NAME,
                            FciCabParams->iCab,
                            &FciCabParams->iDisk
                            );
        if (result) {
            KnownSizeUnicodeToDbcsN (FciCabParams->szCabPath, cabPath, min (CB_MAX_CAB_PATH, CharCountW (cabPath)));
            KnownSizeUnicodeToDbcsN (FciCabParams->szCab, cabFile, min (CB_MAX_CABINET_NAME, CharCountW (cabFile)));
            KnownSizeUnicodeToDbcsN (FciCabParams->szDisk, cabDisk, min (CB_MAX_DISK_NAME, CharCountW (cabDisk)));
            return TRUE;
        }
        return FALSE;
    } else {
        FciCabParams->iDisk = FciCabParams->iCab;
        if (cabHandle->CabFileFormat) {
            wsprintfW (cabFile, cabHandle->CabFileFormat, FciCabParams->iCab);
            KnownSizeUnicodeToDbcsN (FciCabParams->szCab, cabFile, min (CB_MAX_CABINET_NAME, CharCountW (cabFile)));
        }
        if (cabHandle->CabDiskFormat) {
            wsprintfW (cabDisk, cabHandle->CabDiskFormat, FciCabParams->iDisk);
            KnownSizeUnicodeToDbcsN (FciCabParams->szDisk, cabDisk, min (CB_MAX_DISK_NAME, CharCountW (cabDisk)));
        }
    }
    return TRUE;
}

LONG
DIAMONDAPI
pCabStatusA (
    IN      UINT StatusType,
    IN      ULONG Size1,
    IN      ULONG Size2,
    IN      PVOID Context
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    PFCI_CAB_HANDLEA cabHandle;

    if (StatusType == statusCabinet) {

        cabHandle = (PFCI_CAB_HANDLEA) Context;
        if (!cabHandle) {
            return 0;
        }

        cabHandle->CabCount++;
        cabHandle->CompressedSize += (LONGLONG) Size2;
    }

    return 0;
}

LONG
DIAMONDAPI
pCabStatusW (
    IN      UINT StatusType,
    IN      ULONG Size1,
    IN      ULONG Size2,
    IN      PVOID Context
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    PFCI_CAB_HANDLEW cabHandle;

    if (StatusType == statusCabinet) {

        cabHandle = (PFCI_CAB_HANDLEW) Context;
        if (!cabHandle) {
            return 0;
        }

        cabHandle->CabCount++;
        cabHandle->CompressedSize += (LONGLONG) Size2;
    }

    return 0;
}

INT_PTR
DIAMONDAPI
pCabGetOpenInfoA (
    IN      PSTR FileName,
    OUT     USHORT *Date,
    OUT     USHORT *Time,
    OUT     USHORT *Attributes,
    OUT     PINT Error,
    IN      PVOID Context
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    WIN32_FIND_DATAA findData;
    FILETIME fileTime;
    HANDLE fileHandle;

    if (DoesFileExistExA (FileName, &findData)) {

        FileTimeToLocalFileTime (&findData.ftLastWriteTime, &fileTime);
        FileTimeToDosDateTime (&fileTime, Date, Time);

        /*
         * Mask out all other bits except these four, since other
         * bits are used by the cabinet format to indicate a
         * special meaning.
         */
        *Attributes = (USHORT) (findData.dwFileAttributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE));

        fileHandle = CreateFileA (
                        FileName,
                        GENERIC_READ,
                        FILE_SHARE_READ|FILE_SHARE_WRITE,
                        NULL,
                        OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL
                        );
        if (fileHandle == INVALID_HANDLE_VALUE) {
            *Error = GetLastError ();
            return -1;
        }
        *Error = 0;
        return (INT_PTR)fileHandle;
    } else {
        *Error = GetLastError ();
        return -1;
    }
}

INT_PTR
DIAMONDAPI
pCabNotificationA (
    IN      FDINOTIFICATIONTYPE FdiNotificationType,
    IN OUT  PFDINOTIFICATION FdiNotification
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    PCSTR destFile = NULL;
    HANDLE destHandle = NULL;
    DWORD attributes;
    FILETIME localFileTime;
    FILETIME fileTime;
    PCAB_DATAA cabData;

    switch (FdiNotificationType) {
    case fdintCABINET_INFO:     // General information about cabinet
        return 0;
    case fdintCOPY_FILE:        // File to be copied
        cabData = (PCAB_DATAA)FdiNotification->pv;
        destFile = JoinPathsA (cabData->ExtractPath, FdiNotification->psz1);
        if (cabData->CabNotification) {
            if (cabData->CabNotification (destFile)) {
                destHandle = BfCreateFileA (destFile);
            }
        } else {
            destHandle = BfCreateFileA (destFile);
        }
        FreePathStringA (destFile);
        return (INT_PTR)destHandle;
    case fdintCLOSE_FILE_INFO:  // close the file, set relevant info
        cabData = (PCAB_DATAA)FdiNotification->pv;
        if (DosDateTimeToFileTime (FdiNotification->date, FdiNotification->time, &localFileTime)) {
            if (LocalFileTimeToFileTime (&localFileTime, &fileTime)) {
                SetFileTime ((HANDLE)FdiNotification->hf, &fileTime, &fileTime, &fileTime);
            }
        }
        destFile = JoinPathsA (cabData->ExtractPath, FdiNotification->psz1);
        CloseHandle ((HANDLE)FdiNotification->hf);
        attributes = (FdiNotification->attribs & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE));
        SetFileAttributesA (destFile, attributes);
        FreePathStringA (destFile);
        return TRUE;
    case fdintPARTIAL_FILE:     // First file in cabinet is continuation
        return 0;
    case fdintENUMERATE:        // Enumeration status
        return 0;
    case fdintNEXT_CABINET:     // File continued to next cabinet
        // sometimes, in a corrupted cabinet file, the cabinet APIs can ask
        // for the next cabinet but nothing more exists. If we don't fail here
        // in this case, we are going to infinite loop, so do some checking
        if (!FdiNotification) {
            return -1;
        }
        if (FdiNotification->psz1[0] == 0) {
            return -1;
        }
        return 0;
    }
    return 0;
}

INT_PTR
DIAMONDAPI
pCabNotificationW (
    IN      FDINOTIFICATIONTYPE FdiNotificationType,
    IN OUT  PFDINOTIFICATION FdiNotification
    )

/*++

Routine Description:

  Callback for cabinet compression/decompression. For more information see fci.h/fdi.h

--*/

{
    PCWSTR destFile = NULL;
    PCWSTR destFileW = NULL;
    HANDLE destHandle = NULL;
    DWORD attributes;
    FILETIME localFileTime;
    FILETIME fileTime;
    PCAB_DATAW cabData;

    switch (FdiNotificationType) {
    case fdintCABINET_INFO:     // General information about cabinet
        return 0;
    case fdintCOPY_FILE:        // File to be copied
        cabData = (PCAB_DATAW)FdiNotification->pv;
        destFileW = ConvertAtoW (FdiNotification->psz1);
        if (destFileW) {
            destFile = JoinPathsW (cabData->ExtractPath, destFileW);
            if (destFile) {
                if (cabData->CabNotification) {
                    if (cabData->CabNotification (destFile)) {
                        destHandle = BfCreateFileW (destFile);
                    }
                } else {
                    destHandle = BfCreateFileW (destFile);
                }
                FreePathStringW (destFile);
            }
            FreeConvertedStr (destFileW);
        }
        return (INT_PTR)destHandle;
    case fdintCLOSE_FILE_INFO:  // close the file, set relevant info
        cabData = (PCAB_DATAW)FdiNotification->pv;
        if (DosDateTimeToFileTime (FdiNotification->date, FdiNotification->time, &localFileTime)) {
            if (LocalFileTimeToFileTime (&localFileTime, &fileTime)) {
                SetFileTime ((HANDLE)FdiNotification->hf, &fileTime, &fileTime, &fileTime);
            }
        }
        destFileW = ConvertAtoW (FdiNotification->psz1);
        if (destFileW) {
            destFile = JoinPathsW (cabData->ExtractPath, destFileW);
            if (destFile) {
                CloseHandle ((HANDLE)FdiNotification->hf);
                attributes = (FdiNotification->attribs & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE));
                SetFileAttributesW (destFile, attributes);
                FreePathStringW (destFile);
            }
            FreeConvertedStr (destFileW);
        }
        return TRUE;
    case fdintPARTIAL_FILE:     // First file in cabinet is continuation
        return 0;
    case fdintENUMERATE:        // Enumeration status
        return 0;
    case fdintNEXT_CABINET:     // File continued to next cabinet
        // sometimes, in a corrupted cabinet file, the cabinet APIs can ask
        // for the next cabinet but nothing more exists. If we don't fail here
        // in this case, we are going to infinite loop, so do some checking
        if (!FdiNotification) {
            return -1;
        }
        if (FdiNotification->psz1[0] == 0) {
            return -1;
        }
        return 0;
    }
    return 0;
}

CCABHANDLE
CabCreateCabinetByIndexA (
    IN      PCSTR CabPath,
    IN      PCSTR CabFileFormat,
    IN      PCSTR CabDiskFormat,
    IN      PCABGETTEMPFILEA CabGetTempFile, // OPTIONAL
    IN      LONG MaxFileSize,
    IN      INT InitialIndex
    )

/*++

Routine Description:

  Creates a cabinet context. Caller may use this context for subsequent calls to
  CabAddFile.

Arguments:

  CabPath - Specifies the path where the new cabinet file will be.

  CabFileFormat - Specifies (as for wsprintf) the format of the cabinet file name.

  CabDiskFormat - Specifies (as for wsprintf) the format of the cabinet disk name.

  MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB

Return Value:

  a valid CCABHANDLE if successful, NULL otherwise.

--*/

{
    PFCI_CAB_HANDLEA cabHandle;
    CHAR cabFile [1024];
    CHAR cabDisk [1024];

    if (!CabFileFormat) {
        return NULL;
    }

    if (MaxFileSize < 0) {
        return NULL;
    }

    if (MaxFileSize == 0) {
        MaxFileSize = 0x7FFFFFFF;
    }

    cabHandle = (PFCI_CAB_HANDLEA) MemAlloc (g_hHeap, 0, sizeof (FCI_CAB_HANDLEA));
    ZeroMemory (cabHandle, sizeof (FCI_CAB_HANDLEA));
    if (CabPath) {
        cabHandle->CabPath = DuplicatePathStringA (CabPath, 0);
    }
    cabHandle->CabFileFormat = DuplicatePathStringA (CabFileFormat, 0);
    if (CabDiskFormat) {
        cabHandle->CabDiskFormat = DuplicatePathStringA (CabDiskFormat, 0);
    }
    cabHandle->CabGetTempFile = CabGetTempFile;

    // fill out the CCAB structure
    cabHandle->FciCabParams.cb = MaxFileSize;
    cabHandle->FciCabParams.cbFolderThresh = MaxFileSize;
    cabHandle->FciCabParams.cbReserveCFHeader = 0;
    cabHandle->FciCabParams.cbReserveCFFolder = 0;
    cabHandle->FciCabParams.cbReserveCFData = 0;
    cabHandle->FciCabParams.iCab = InitialIndex;
    cabHandle->FciCabParams.iDisk = InitialIndex;
    cabHandle->FciCabParams.setID = 0;
    if (CabPath) {
        StringCopyByteCountA (cabHandle->FciCabParams.szCabPath, CabPath, CB_MAX_CAB_PATH - 1);
        AppendWackA (cabHandle->FciCabParams.szCabPath);
    }
    if (CabDiskFormat) {
        wsprintfA (cabDisk, CabDiskFormat, cabHandle->FciCabParams.iDisk);
        StringCopyByteCountA (cabHandle->FciCabParams.szDisk, cabDisk, CB_MAX_DISK_NAME * sizeof (CHAR));
    }
    wsprintfA (cabFile, CabFileFormat, cabHandle->FciCabParams.iCab);
    StringCopyByteCountA (cabHandle->FciCabParams.szCab, cabFile, CB_MAX_CABINET_NAME * sizeof (CHAR));

    cabHandle->FciHandle = FCICreate (
                                &cabHandle->FciErrorStruct,
                                pCabFilePlacedA,
                                pCabAlloc,
                                pCabFree,
                                pCabOpenA,
                                pCabRead,
                                pCabWrite,
                                pCabClose,
                                pCabSeek,
                                pCabDeleteA,
                                pCabGetTempFileA,
                                &cabHandle->FciCabParams,
                                cabHandle
                                );
    if (!cabHandle->FciHandle) {
        if (cabHandle->CabPath) {
            FreePathStringA (cabHandle->CabPath);
        }
        if (cabHandle->CabFileFormat) {
            FreePathStringA (cabHandle->CabFileFormat);
        }
        if (cabHandle->CabDiskFormat) {
            FreePathStringA (cabHandle->CabDiskFormat);
        }
        MemFree (g_hHeap, 0, cabHandle);
        return NULL;
    }
    return ((CCABHANDLE)(cabHandle));
}

CCABHANDLE
CabCreateCabinetByIndexW (
    IN      PCWSTR CabPath,
    IN      PCWSTR CabFileFormat,
    IN      PCWSTR CabDiskFormat,
    IN      PCABGETTEMPFILEW CabGetTempFile, // OPTIONAL
    IN      LONG MaxFileSize,
    IN      INT InitialIndex
    )

/*++

Routine Description:

  Creates a cabinet context. Caller may use this context for subsequent calls to
  CabAddFile.

Arguments:

  CabPath - Specifies the path where the new cabinet file will be.

  CabFileFormat - Specifies (as for wsprintf) the format of the cabinet file name.

  CabDiskFormat - Specifies (as for wsprintf) the format of the cabinet disk name.

  MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB

Return Value:

  a valid CCABHANDLE if successful, NULL otherwise.

--*/

{
    PFCI_CAB_HANDLEW cabHandle;
    WCHAR cabFile [1024];
    WCHAR cabDisk [1024];

    if (!CabFileFormat) {
        return NULL;
    }

    if (MaxFileSize < 0) {
        return NULL;
    }

    if (MaxFileSize == 0) {
        MaxFileSize = 0x7FFFFFFF;
    }

    cabHandle = (PFCI_CAB_HANDLEW) MemAlloc (g_hHeap, 0, sizeof (FCI_CAB_HANDLEW));
    ZeroMemory (cabHandle, sizeof (FCI_CAB_HANDLEW));
    if (CabPath) {
        cabHandle->CabPath = DuplicatePathStringW (CabPath, 0);
    }
    cabHandle->CabFileFormat = DuplicatePathStringW (CabFileFormat, 0);
    if (CabDiskFormat) {
        cabHandle->CabDiskFormat = DuplicatePathStringW (CabDiskFormat, 0);
    }
    cabHandle->CabGetTempFile = CabGetTempFile;

    // fill out the CCAB structure
    cabHandle->FciCabParams.cb = MaxFileSize;
    cabHandle->FciCabParams.cbFolderThresh = MaxFileSize;
    cabHandle->FciCabParams.cbReserveCFHeader = 0;
    cabHandle->FciCabParams.cbReserveCFFolder = 0;
    cabHandle->FciCabParams.cbReserveCFData = 0;
    cabHandle->FciCabParams.iCab = InitialIndex;
    cabHandle->FciCabParams.iDisk = InitialIndex;
    cabHandle->FciCabParams.setID = 0;
    if (CabPath) {
        KnownSizeUnicodeToDbcsN (cabHandle->FciCabParams.szCabPath, CabPath, min (CB_MAX_CAB_PATH - 1, CharCountW (CabPath)));
        AppendWackA (cabHandle->FciCabParams.szCabPath);
    }
    if (CabDiskFormat) {
        wsprintfW (cabDisk, CabDiskFormat, cabHandle->FciCabParams.iDisk);
        KnownSizeUnicodeToDbcsN (cabHandle->FciCabParams.szDisk, cabDisk, min (CB_MAX_DISK_NAME, CharCountW (cabDisk)));
    }
    wsprintfW (cabFile, CabFileFormat, cabHandle->FciCabParams.iCab);
    KnownSizeUnicodeToDbcsN (cabHandle->FciCabParams.szCab, cabFile, min (CB_MAX_CABINET_NAME, CharCountW (cabFile)));

    cabHandle->FciHandle = FCICreate (
                                &cabHandle->FciErrorStruct,
                                pCabFilePlacedW,
                                pCabAlloc,
                                pCabFree,
                                pCabOpenA,
                                pCabRead,
                                pCabWrite,
                                pCabClose,
                                pCabSeek,
                                pCabDeleteA,
                                pCabGetTempFileW,
                                &cabHandle->FciCabParams,
                                cabHandle
                                );
    if (!cabHandle->FciHandle) {
        if (cabHandle->CabPath) {
            FreePathStringW (cabHandle->CabPath);
        }
        if (cabHandle->CabFileFormat) {
            FreePathStringW (cabHandle->CabFileFormat);
        }
        if (cabHandle->CabDiskFormat) {
            FreePathStringW (cabHandle->CabDiskFormat);
        }
        MemFree (g_hHeap, 0, cabHandle);
        return NULL;
    }
    return ((CCABHANDLE)(cabHandle));
}

CCABHANDLE
CabCreateCabinetExA (
    IN      PCABGETCABINETNAMESA CabGetCabinetNames,
    IN      LONG MaxFileSize
    )

/*++

Routine Description:

  Creates a cabinet context. Caller may use this context for subsequent calls to
  CabAddFile.

Arguments:

  CabGetCabinetNames - Specifies a callback used to decide cabinet path, cabinet name and disk name.

  MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB

Return Value:

  a valid CCABHANDLE if successful, NULL otherwise.

--*/

{
    PFCI_CAB_HANDLEA cabHandle;

    if (!CabGetCabinetNames) {
        return NULL;
    }

    if (MaxFileSize < 0) {
        return NULL;
    }

    if (MaxFileSize == 0) {
        MaxFileSize = 0x80000000;
    }

    cabHandle = MemAlloc (g_hHeap, 0, sizeof (FCI_CAB_HANDLEA));
    ZeroMemory (cabHandle, sizeof (FCI_CAB_HANDLEA));
    cabHandle->CabGetCabinetNames = CabGetCabinetNames;

    // fill out the CCAB structure
    cabHandle->FciCabParams.cb = MaxFileSize;
    cabHandle->FciCabParams.cbFolderThresh = MaxFileSize;
    cabHandle->FciCabParams.cbReserveCFHeader = 0;
    cabHandle->FciCabParams.cbReserveCFFolder = 0;
    cabHandle->FciCabParams.cbReserveCFData = 0;
    cabHandle->FciCabParams.iCab = 1;
    cabHandle->FciCabParams.iDisk = 1;
    cabHandle->FciCabParams.setID = 0;
    if (!CabGetCabinetNames (
            cabHandle->FciCabParams.szCabPath,
            CB_MAX_CAB_PATH,
            cabHandle->FciCabParams.szCab,
            CB_MAX_CABINET_NAME,
            cabHandle->FciCabParams.szDisk,
            CB_MAX_DISK_NAME,
            cabHandle->FciCabParams.iCab,
            &cabHandle->FciCabParams.iDisk
            )) {
        return NULL;
    }
    cabHandle->FciHandle = FCICreate (
                                &cabHandle->FciErrorStruct,
                                pCabFilePlacedA,
                                pCabAlloc,
                                pCabFree,
                                pCabOpenA,
                                pCabRead,
                                pCabWrite,
                                pCabClose,
                                pCabSeek,
                                pCabDeleteA,
                                pCabGetTempFileA,
                                &cabHandle->FciCabParams,
                                cabHandle
                                );
    if (!cabHandle->FciHandle) {
        if (cabHandle->CabPath) {
            FreePathStringA (cabHandle->CabPath);
        }
        if (cabHandle->CabFileFormat) {
            FreePathStringA (cabHandle->CabFileFormat);
        }
        if (cabHandle->CabDiskFormat) {
            FreePathStringA (cabHandle->CabDiskFormat);
        }
        MemFree (g_hHeap, 0, cabHandle);
        return NULL;
    }
    return ((CCABHANDLE)(cabHandle));
}

CCABHANDLE
CabCreateCabinetExW (
    IN      PCABGETCABINETNAMESW CabGetCabinetNames,
    IN      LONG MaxFileSize
    )

/*++

Routine Description:

  Creates a cabinet context. Caller may use this context for subsequent calls to
  CabAddFile.

Arguments:

  CabGetCabinetNames - Specifies a callback used to decide cabinet path, cabinet name and disk name.

  MaxFileSize - Specifies maximum size of the cabinet file (limited to 2GB). if 0 => 2GB

Return Value:

  a valid CCABHANDLE if successful, NULL otherwise.

--*/

{
    PFCI_CAB_HANDLEW cabHandle;
    WCHAR cabPath [1024];
    WCHAR cabFile [1024];
    WCHAR cabDisk [1024];

    if (!CabGetCabinetNames) {
        return NULL;
    }

    if (MaxFileSize < 0) {
        return NULL;
    }

    if (MaxFileSize == 0) {
        MaxFileSize = 0x80000000;
    }

    cabHandle = MemAlloc (g_hHeap, 0, sizeof (FCI_CAB_HANDLEW));
    ZeroMemory (cabHandle, sizeof (FCI_CAB_HANDLEW));
    cabHandle->CabGetCabinetNames = CabGetCabinetNames;

    // fill out the CCAB structure
    cabHandle->FciCabParams.cb = MaxFileSize;
    cabHandle->FciCabParams.cbFolderThresh = MaxFileSize;
    cabHandle->FciCabParams.cbReserveCFHeader = 0;
    cabHandle->FciCabParams.cbReserveCFFolder = 0;
    cabHandle->FciCabParams.cbReserveCFData = 0;
    cabHandle->FciCabParams.iCab = 1;
    cabHandle->FciCabParams.iDisk = 1;
    cabHandle->FciCabParams.setID = 0;
    if (!CabGetCabinetNames (
            cabPath,
            CB_MAX_CAB_PATH,
            cabFile,
            CB_MAX_CABINET_NAME,
            cabDisk,
            CB_MAX_DISK_NAME,
            cabHandle->FciCabParams.iCab,
            &cabHandle->FciCabParams.iDisk
            )) {
        return NULL;
    }
    KnownSizeUnicodeToDbcsN (cabHandle->FciCabParams.szCabPath, cabPath, min (CB_MAX_CAB_PATH, CharCountW (cabPath)));
    KnownSizeUnicodeToDbcsN (cabHandle->FciCabParams.szCab, cabFile, min (CB_MAX_CABINET_NAME, CharCountW (cabFile)));
    KnownSizeUnicodeToDbcsN (cabHandle->FciCabParams.szDisk, cabDisk, min (CB_MAX_DISK_NAME, CharCountW (cabDisk)));
    cabHandle->FciHandle = FCICreate (
                                &cabHandle->FciErrorStruct,
                                pCabFilePlacedW,
                                pCabAlloc,
                                pCabFree,
                                pCabOpenA,
                                pCabRead,
                                pCabWrite,
                                pCabClose,
                                pCabSeek,
                                pCabDeleteA,
                                pCabGetTempFileW,
                                &cabHandle->FciCabParams,
                                cabHandle
                                );
    if (!cabHandle->FciHandle) {
        if (cabHandle->CabPath) {
            FreePathStringW (cabHandle->CabPath);
        }
        if (cabHandle->CabFileFormat) {
            FreePathStringW (cabHandle->CabFileFormat);
        }
        if (cabHandle->CabDiskFormat) {
            FreePathStringW (cabHandle->CabDiskFormat);
        }
        MemFree (g_hHeap, 0, cabHandle);
        return NULL;
    }
    return ((CCABHANDLE)(cabHandle));
}

BOOL
CabAddFileToCabinetA (
    IN      CCABHANDLE CabHandle,
    IN      PCSTR FileName,
    IN      PCSTR StoredName
    )

/*++

Routine Description:

  Compresses and adds a file to a cabinet context.

Arguments:

  CabHandle - Specifies cabinet context.

  FileName - Specifies the file to be added.

  StoredName - Specifies the name to be stored in the cabinet file.

Return Value:

  TRUE if successful, FALSE otherwise.

--*/

{
    PFCI_CAB_HANDLEA cabHandle;

    cabHandle = (PFCI_CAB_HANDLEA) CabHandle;
    if (cabHandle == NULL) {
        return FALSE;
    }
    if (cabHandle->FciHandle == NULL) {
        return FALSE;
    }

    return FCIAddFile (
                cabHandle->FciHandle,
                (PSTR)FileName,
                (PSTR)StoredName,
                FALSE,
                pCabGetNextCabinetA,
                pCabStatusA,
                pCabGetOpenInfoA,
                tcompTYPE_MSZIP
                );
}

BOOL
CabAddFileToCabinetW (
    IN      CCABHANDLE CabHandle,
    IN      PCWSTR FileName,
    IN      PCWSTR StoredName
    )

/*++

Routine Description:

  Compresses and adds a file to a cabinet context.

Arguments:

  CabHandle - Specifies cabinet context.

  FileName - Specifies the file to be added.

  StoredName - Specifies the name to be stored in the cabinet file.

  FileCount - Specifies a count of files, receives the updated count
              when cabinet files are created

  FileSize - Specifies the number of bytes used by the file, receives
             the updated size

Return Value:

  TRUE if successful, FALSE otherwise.

--*/

{
    PFCI_CAB_HANDLEW cabHandle;
    CHAR ansiFileName [1024];
    CHAR ansiStoredName [1024];

    cabHandle = (PFCI_CAB_HANDLEW) CabHandle;
    if (cabHandle == NULL) {
        return FALSE;
    }
    if (cabHandle->FciHandle == NULL) {
        return FALSE;
    }
    KnownSizeUnicodeToDbcsN (ansiFileName, FileName, min (CB_MAX_CABINET_NAME, CharCountW (FileName)));
    KnownSizeUnicodeToDbcsN (ansiStoredName, StoredName, min (CB_MAX_CABINET_NAME, CharCountW (StoredName)));

    return FCIAddFile (
                cabHandle->FciHandle,
                ansiFileName,
                ansiStoredName,
                FALSE,
                pCabGetNextCabinetW,
                pCabStatusW,
                pCabGetOpenInfoA,
                tcompTYPE_MSZIP
                );
}

BOOL
CabFlushAndCloseCabinetExA (
    IN      CCABHANDLE CabHandle,
    OUT     PUINT FileCount,        OPTIONAL
    OUT     PLONGLONG FileSize,     OPTIONAL
    OUT     PUINT CabFileCount,     OPTIONAL
    OUT     PLONGLONG CabFileSize   OPTIONAL
    )

/*++

Routine Description:

  Completes a cabinet file and closes its context.

Arguments:

  CabHandle - Specifies cabinet context.

  FileCount - Receives the number of files added to the cab

  FileSize - Receives the size of all files before compression

  CabFileCount - Receives the number of cabinet files created

  CabFileSize - Receives the size of all cabinet files

Return Value:

  TRUE if successful, FALSE otherwise.

--*/

{
    PFCI_CAB_HANDLEA cabHandle;
    BOOL result = FALSE;

    cabHandle = (PFCI_CAB_HANDLEA) CabHandle;
    if (cabHandle == NULL) {
        return FALSE;
    }
    if (cabHandle->FciHandle == NULL) {
        return FALSE;
    }
    if (FCIFlushCabinet (
            cabHandle->FciHandle,
            FALSE,
            pCabGetNextCabinetA,
            pCabStatusA
            )) {
        if (cabHandle->CabPath) {
            FreePathStringA (cabHandle->CabPath);
        }
        if (cabHandle->CabFileFormat) {
            FreePathStringA (cabHandle->CabFileFormat);
        }
        if (cabHandle->CabDiskFormat) {
            FreePathStringA (cabHandle->CabDiskFormat);
        }
        result = FCIDestroy (cabHandle->FciHandle);

        if (FileCount) {
            *FileCount = cabHandle->FileCount;
        }

        if (FileSize) {
            *FileSize = cabHandle->FileSize;
        }

        if (CabFileCount) {
            *CabFileCount = cabHandle->CabCount;
        }

        if (CabFileSize) {
            *CabFileSize = cabHandle->CompressedSize;
        }

        MemFree (g_hHeap, 0, cabHandle);
    }

    return result;
}

BOOL
CabFlushAndCloseCabinetExW (
    IN      CCABHANDLE CabHandle,
    OUT     PUINT FileCount,        OPTIONAL
    OUT     PLONGLONG FileSize,     OPTIONAL
    OUT     PUINT CabFileCount,     OPTIONAL
    OUT     PLONGLONG CabFileSize   OPTIONAL
    )

/*++

Routine Description:

  Completes a cabinet file and closes its context.

Arguments:

  CabHandle - Specifies cabinet context.

  FileCount - Receives the number of files added to the cab

  FileSize - Receives the size of all files before compression

  CabFileCount - Receives the number of cabinet files created

  CabFileSize - Receives the size of all cabinet files

Return Value:

  TRUE if successful, FALSE otherwise.

--*/

{
    PFCI_CAB_HANDLEW cabHandle;
    BOOL result = FALSE;

    cabHandle = (PFCI_CAB_HANDLEW) CabHandle;
    if (cabHandle == NULL) {
        return FALSE;
    }
    if (cabHandle->FciHandle == NULL) {
        return FALSE;
    }
    if (FCIFlushCabinet (
            cabHandle->FciHandle,
            FALSE,
            pCabGetNextCabinetW,
            pCabStatusW
            )) {
        if (cabHandle->CabPath) {
            FreePathStringW (cabHandle->CabPath);
        }
        if (cabHandle->CabFileFormat) {
            FreePathStringW (cabHandle->CabFileFormat);
        }
        if (cabHandle->CabDiskFormat) {
            FreePathStringW (cabHandle->CabDiskFormat);
        }
        result = FCIDestroy (cabHandle->FciHandle);

        if (FileCount) {
            *FileCount = cabHandle->FileCount;
        }

        if (FileSize) {
            *FileSize = cabHandle->FileSize;
        }

        if (CabFileCount) {
            *CabFileCount = cabHandle->CabCount;
        }

        if (CabFileSize) {
            *CabFileSize = cabHandle->CompressedSize;
        }

        MemFree (g_hHeap, 0, cabHandle);
    }
    return result;
}

OCABHANDLE
CabOpenCabinetA (
    IN      PCSTR FileName
    )

/*++

Routine Description:

  Creates a cabinet context for an existent cabinet file.

Arguments:

  FileName - Specifies cabinet file name.

Return Value:

  a valid OCABHANDLE if successful, NULL otherwise.

--*/

{
    PFDI_CAB_HANDLEA cabHandle;
    PSTR filePtr;
    HANDLE fileHandle;
    PCSTR fileName;

    cabHandle = (PFDI_CAB_HANDLEA) MemAlloc (g_hHeap, 0, sizeof (FDI_CAB_HANDLEA));
    ZeroMemory (cabHandle, sizeof (FDI_CAB_HANDLEA));
    cabHandle->FdiHandle = FDICreate (
                                pCabAlloc,
                                pCabFree,
                                pCabOpen1A,
                                pCabRead1,
                                pCabWrite1,
                                pCabClose1,
                                pCabSeek1,
                                cpuUNKNOWN,
                                &cabHandle->FdiErrorStruct
                                );
    if (!cabHandle->FdiHandle) {
        MemFree (g_hHeap, 0, cabHandle);
        return NULL;
    }
    fileName = DuplicatePathStringA (FileName, 0);
    fileHandle = BfOpenReadFileA (fileName);
    if (!fileHandle) {
        FreePathStringA (fileName);
        MemFree (g_hHeap, 0, cabHandle);
        return NULL;
    }
    if (!FDIIsCabinet (cabHandle->FdiHandle, (INT_PTR)fileHandle, &cabHandle->FdiCabinetInfo)) {
        FreePathStringA (fileName);
        CloseHandle (fileHandle);
        MemFree (g_hHeap, 0, cabHandle);
        return NULL;
    }
    CloseHandle (fileHandle);
    filePtr = (PSTR)GetFileNameFromPathA (fileName);
    if (!filePtr) {
        FreePathStringA (fileName);
        MemFree (g_hHeap, 0, cabHandle);
        return NULL;
    }
    cabHandle->CabFile = DuplicatePathStringA (filePtr, 0);
    *filePtr = 0;
    cabHandle->CabPath = DuplicatePathStringA (fileName, 0);
    FreePathStringA (fileName);
    return ((CCABHANDLE)(cabHandle));
}

OCABHANDLE
CabOpenCabinetW (
    IN      PCWSTR FileName
    )

/*++

Routine Description:

  Creates a cabinet context for an existent cabinet file.

Arguments:

  FileName - Specifies cabinet file name.

Return Value:

  a valid OCABHANDLE if successful, NULL otherwise.

--*/

{
    PFDI_CAB_HANDLEW cabHandle;
    PWSTR filePtr;
    HANDLE fileHandle;
    PCWSTR fileName;
    PCSTR filePathA = NULL;

    cabHandle = (PFDI_CAB_HANDLEW) MemAlloc (g_hHeap, 0, sizeof (FDI_CAB_HANDLEW));
    ZeroMemory (cabHandle, sizeof (FDI_CAB_HANDLEW));
    cabHandle->FdiHandle = FDICreate (
                                pCabAlloc,
                                pCabFree,
                                pCabOpen1W,
                                pCabRead1,
                                pCabWrite1,
                                pCabClose1,
                                pCabSeek1,
                                cpuUNKNOWN,
                                &cabHandle->FdiErrorStruct
                                );
    if (!cabHandle->FdiHandle) {
        MemFree (g_hHeap, 0, cabHandle);
        return NULL;
    }
    fileName = DuplicatePathStringW (FileName, 0);
    fileHandle = BfOpenReadFileW (fileName);
    if (!fileHandle) {
        FreePathStringW (fileName);
        MemFree (g_hHeap, 0, cabHandle);
        return NULL;
    }
    if (!FDIIsCabinet (cabHandle->FdiHandle, (INT_PTR)fileHandle, &cabHandle->FdiCabinetInfo)) {
        FreePathStringW (fileName);
        CloseHandle (fileHandle);
        MemFree (g_hHeap, 0, cabHandle);
        return NULL;
    }
    CloseHandle (fileHandle);
    filePtr = (PWSTR)GetFileNameFromPathW (fileName);
    if (!filePtr) {
        FreePathStringW (fileName);
        MemFree (g_hHeap, 0, cabHandle);
        return NULL;
    }
    cabHandle->CabFile = DuplicatePathStringW (filePtr, 0);
    *filePtr = 0;
    cabHandle->CabPath = DuplicatePathStringW (fileName, 0);
    FreePathStringW (fileName);
    if (!g_UnicodeTable) {
        g_UnicodeTable = HtAllocWithDataA (sizeof (PFDI_CAB_HANDLEW));
    }
    if (g_UnicodeTable) {
        filePathA = ConvertWtoA (cabHandle->CabPath);
        if (filePathA) {
            HtAddStringAndDataA (g_UnicodeTable, filePathA, &cabHandle);
            FreeConvertedStr (filePathA);
            filePathA = NULL;
        }
    }
    return ((CCABHANDLE)(cabHandle));
}

BOOL
CabExtractAllFilesExA (
    IN      OCABHANDLE CabHandle,
    IN      PCSTR ExtractPath,
    IN      PCABNOTIFICATIONA CabNotification   OPTIONAL
    )

/*++

Routine Description:

  Extracts all files from a cabinet file.

Arguments:

  CabHandle - Specifies cabinet context.

  ExtractPath - Specifies the path to extract the files to.

Return Value:

  TRUE if successful, FALSE otherwise.

--*/

{
    PFDI_CAB_HANDLEA cabHandle;
    CAB_DATAA cabData;

    cabHandle = (PFDI_CAB_HANDLEA)CabHandle;
    if (!cabHandle) {
        return FALSE;
    }
    if (!cabHandle->FdiHandle) {
        return FALSE;
    }
    cabData.ExtractPath = ExtractPath;
    cabData.CabNotification = CabNotification;
    return FDICopy (
                cabHandle->FdiHandle,
                (PSTR)cabHandle->CabFile,
                (PSTR)cabHandle->CabPath,
                0,
                pCabNotificationA,
                NULL,
                (PVOID)(&cabData)
                );
}


BOOL
CabExtractAllFilesExW (
    IN      OCABHANDLE CabHandle,
    IN      PCWSTR ExtractPath,
    IN      PCABNOTIFICATIONW CabNotification   OPTIONAL
    )

/*++

Routine Description:

  Extracts all files from a cabinet file.

Arguments:

  CabHandle - Specifies cabinet context.

  ExtractPath - Specifies the path to extract the files to.

Return Value:

  TRUE if successful, FALSE otherwise.

--*/

{
    PFDI_CAB_HANDLEW cabHandle;
    CAB_DATAW cabData;
    PCSTR cabFileA = NULL;
    PCSTR cabPathA = NULL;
    BOOL result = FALSE;

    cabHandle = (PFDI_CAB_HANDLEW)CabHandle;
    if (!cabHandle) {
        return FALSE;
    }
    if (!cabHandle->FdiHandle) {
        return FALSE;
    }
    cabData.ExtractPath = ExtractPath;
    cabData.CabNotification = CabNotification;
    cabFileA = ConvertWtoA (cabHandle->CabFile);
    cabPathA = ConvertWtoA (cabHandle->CabPath);
    if (cabFileA && cabPathA) {
        result = FDICopy (
                    cabHandle->FdiHandle,
                    (PSTR)cabFileA,
                    (PSTR)cabPathA,
                    0,
                    pCabNotificationW,
                    NULL,
                    (PVOID)(&cabData)
                    );
    }
    if (cabPathA) {
        FreeConvertedStr (cabPathA);
        cabPathA = NULL;
    }
    if (cabFileA) {
        FreeConvertedStr (cabFileA);
        cabFileA = NULL;
    }
    return result;
}

BOOL
CabCloseCabinetA (
    IN      OCABHANDLE CabHandle
    )

/*++

Routine Description:

  Closes a cabinet file context.

Arguments:

  CabHandle - Specifies cabinet context.

Return Value:

  TRUE if successful, FALSE otherwise.

--*/

{
    PFDI_CAB_HANDLEA cabHandle;

    cabHandle = (PFDI_CAB_HANDLEA) CabHandle;
    if (!cabHandle) {
        return FALSE;
    }
    if (!cabHandle->FdiHandle) {
        return FALSE;
    }
    if (FDIDestroy (cabHandle->FdiHandle)) {
        if (cabHandle->CabPath) {
            FreePathStringA (cabHandle->CabPath);
        }
        if (cabHandle->CabFile) {
            FreePathStringA (cabHandle->CabFile);
        }
        MemFree (g_hHeap, 0, cabHandle);
        return TRUE;
    }
    return FALSE;
}

BOOL
CabCloseCabinetW (
    IN      OCABHANDLE CabHandle
    )

/*++

Routine Description:

  Closes a cabinet file context.

Arguments:

  CabHandle - Specifies cabinet context.

Return Value:

  TRUE if successful, FALSE otherwise.

--*/

{
    PFDI_CAB_HANDLEW cabHandle;
    PCSTR filePathA = NULL;

    cabHandle = (PFDI_CAB_HANDLEW) CabHandle;
    if (!cabHandle) {
        return FALSE;
    }
    if (!cabHandle->FdiHandle) {
        return FALSE;
    }
    if (FDIDestroy (cabHandle->FdiHandle)) {
        if (cabHandle->CabPath) {
            if (g_UnicodeTable) {
                filePathA = ConvertWtoA (cabHandle->CabPath);
                if (filePathA) {
                    HtRemoveStringA (g_UnicodeTable, filePathA);
                    FreeConvertedStr (filePathA);
                    filePathA = NULL;
                }
            }
            FreePathStringW (cabHandle->CabPath);
        }
        if (cabHandle->CabFile) {
            FreePathStringW (cabHandle->CabFile);
        }
        MemFree (g_hHeap, 0, cabHandle);
        return TRUE;
    }
    return FALSE;
}