/*++

Copyright (c) 1998  Microsoft Corporation

Module Name:

    openclose.c

Abstract:

    This module contains the code to open and
    close a cab file.

Author:

    Wesley Witt (wesw) 29-Sept-1998

Revision History:

--*/

#include <ntcabp.h>
#pragma hdrstop


BOOL
NtCabOpenCabFile(
    IN PCAB_INSTANCE_DATA CabInst,
    IN PCWSTR CabFileName
    )
{
    BOOL rVal;
    HANDLE hCab;
    DWORD Bytes;
    DWORD FileSize;
    PULONG Lengths;
    DWORD i;
    PLIST_ENTRY Next;
    PCAB_DIR_ENTRY CabDir;
    CAB_HEADER CabHeader;


    CabInst->hCab = CreateFile(
        CabFileName,
        GENERIC_WRITE | GENERIC_READ,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL
        );
    if (CabInst->hCab == INVALID_HANDLE_VALUE) {
        return FALSE;
    }

    FileSize = GetFileSize( CabInst->hCab, NULL );

    rVal = ReadFile(
        CabInst->hCab,
        &CabHeader,
        sizeof(CAB_HEADER),
        &Bytes,
        NULL
        );
    if (!rVal) {
        CloseHandle( CabInst->hCab );
        return FALSE;
    }

    if ((CabHeader.Signature != NTCAB_SIGNATURE) ||
        (CabHeader.Version != NTCAB_VERSION))
    {
        return FALSE;
    }

    CabInst->CabDirOffset = CabHeader.DirOffset;

    if (SetFilePointer( CabInst->hCab, CabInst->CabDirOffset, NULL, FILE_BEGIN ) == 0xffffffff) {
        CloseHandle( CabInst->hCab );
        return FALSE;
    }

    InitializeListHead( &CabInst->CabDir );

    rVal = ReadFile(
        CabInst->hCab,
        &CabInst->CabNum,
        sizeof(ULONG),
        &Bytes,
        NULL
        );
    Lengths = (PULONG) malloc( CabInst->CabNum * sizeof(ULONG) );
    if (Lengths == NULL) {
        CloseHandle( CabInst->hCab );
        return FALSE;
    }
    rVal = ReadFile(
        CabInst->hCab,
        Lengths,
        CabInst->CabNum * sizeof(ULONG),
        &Bytes,
        NULL
        );
    for (i=0; i<CabInst->CabNum; i++) {
        Bytes = sizeof(CAB_DIR_ENTRY) + (sizeof(ULONG)*Lengths[i]);
        CabDir = (PCAB_DIR_ENTRY) malloc( Bytes );
        if (CabDir == NULL) {
            CloseHandle( CabInst->hCab );
            return FALSE;
        }
        rVal = ReadFile(
            CabInst->hCab,
            CabDir,
            Lengths[i],
            &Bytes,
            NULL
            );
        ZeroMemory( &CabDir->Next, sizeof(LIST_ENTRY) );
        InsertTailList( &CabInst->CabDir, &CabDir->Next );
    }

    CabInst->CabFileName = _wcsdup( CabFileName );

    return TRUE;
}


BOOL
NtCabClose(
    IN PCAB_INSTANCE_DATA CabInst
    )
{
    NTSTATUS Status;
    DWORD Bytes;
    DWORD DirOffset;
    PULONG Lengths;
    DWORD i;
    PLIST_ENTRY Next;
    PCAB_DIR_ENTRY CabDir;
    CAB_HEADER CabHeader;


    if (CabInst->NewCabFile) {
        if (IsListEmpty(&CabInst->CabDir)) {
            return FALSE;
        }
        DirOffset = SetFilePointer( CabInst->hCab, 0, NULL, FILE_END );
        Lengths = (PULONG) malloc( CabInst->CabNum * sizeof(ULONG) );
        if (Lengths == NULL) {
            CloseHandle( CabInst->hCab );
            return FALSE;
        }
        i = 0;
        Next = CabInst->CabDir.Flink;
        while ((ULONG_PTR)Next != (ULONG_PTR)&CabInst->CabDir) {
            CabDir = CONTAINING_RECORD( Next, CAB_DIR_ENTRY, Next );
            Next = CabDir->Next.Flink;
            Lengths[i] = (CabDir->Segments * sizeof(ULONG)) + sizeof(CAB_DIR_ENTRY);
            i += 1;
        }
        Status = WriteFile(
            CabInst->hCab,
            &CabInst->CabNum,
            sizeof(ULONG),
            &Bytes,
            NULL
            );
        Status = WriteFile(
            CabInst->hCab,
            Lengths,
            CabInst->CabNum * sizeof(ULONG),
            &Bytes,
            NULL
            );
        i = 0;
        Next = CabInst->CabDir.Flink;
        while ((ULONG_PTR)Next != (ULONG_PTR)&CabInst->CabDir) {
            CabDir = CONTAINING_RECORD( Next, CAB_DIR_ENTRY, Next );
            Next = CabDir->Next.Flink;
            Status = WriteFile(
                CabInst->hCab,
                CabDir,
                Lengths[i++],
                &Bytes,
                NULL
                );
        }
        SetFilePointer( CabInst->hCab, 0, NULL, FILE_BEGIN );

        CabHeader.Signature = NTCAB_SIGNATURE;
        CabHeader.Version = NTCAB_VERSION;
        CabHeader.DirOffset = DirOffset;

        Status = WriteFile(
            CabInst->hCab,
            &CabHeader,
            sizeof(CAB_HEADER),
            &Bytes,
            NULL
            );
    }

    CloseHandle( CabInst->hCab );

    return TRUE;
}