/*++

Module Name:

    diamond.c

Abstract:

    Diamond compression interface.

    This module contains functions to compress a file using
    the mszip compression library.

Author:

    Ted Miller

Environment:

    Windows

--*/


#include <windows.h>
#include <stdio.h>

#include "lz_common.h"
#include "lz_buffers.h"
#include "lz_header.h"

#include <io.h>
#include <fcntl.h>
#include <errno.h>

#include "main.h"
#include <diamondc.h>
#include "mydiam.h"


typedef struct _DIAMOND_INFO {
    DWORD SourceFileSize;
    DWORD CompressedSize;
    FILETIME SourceFileCreationTime;
    FILETIME SourceFileModifiedTime;
    FILETIME SourceFileAccessedTime;
} DIAMOND_INFO, *PDIAMOND_INFO;

//
// Callback functions to perform memory allocation, io, etc.
// We pass addresses of these functions to diamond.
//
int
DIAMONDAPI
fciFilePlacedCB(
    OUT PCCAB Cabinet,
    IN  PSTR  FileName,
    IN  LONG  FileSize,
    IN  BOOL  Continuation,
    IN  PVOID Context
    )

/*++

Routine Description:

    Callback used by diamond to indicate that a file has been
    comitted to a cabinet.

    No action is taken and success is returned.

Arguments:

    Cabinet - cabinet structure to fill in.

    FileName - name of file in cabinet

    FileSize - size of file in cabinet

    Continuation - TRUE if this is a partial file, continuation
        of compression begun in a different cabinet.

    Context - supplies context information.

Return Value:

    0 (success).

--*/

{
    UNREFERENCED_PARAMETER(Cabinet);
    UNREFERENCED_PARAMETER(FileName);
    UNREFERENCED_PARAMETER(FileSize);
    UNREFERENCED_PARAMETER(Continuation);
    UNREFERENCED_PARAMETER(Context);

    return(0);
}



PVOID
DIAMONDAPI
fciAllocCB(
    IN ULONG NumberOfBytes
    )

/*++

Routine Description:

    Callback used by diamond to allocate memory.

Arguments:

    NumberOfBytes - supplies desired size of block.

Return Value:

    Returns pointer to a block of memory or NULL
    if memory cannot be allocated.

--*/

{
    return((PVOID)LocalAlloc(LMEM_FIXED,NumberOfBytes));
}


VOID
DIAMONDAPI
fciFreeCB(
    IN PVOID Block
    )

/*++

Routine Description:

    Callback used by diamond to free a memory block.
    The block must have been allocated with fciAlloc().

Arguments:

    Block - supplies pointer to block of memory to be freed.

Return Value:

    None.

--*/

{
    LocalFree((HLOCAL)Block);
}



FNFCIGETTEMPFILE(fciTempFileCB)
{
    CHAR TempPath[MAX_PATH];
    DWORD cchTemp;

    cchTemp = GetTempPath(sizeof(TempPath), TempPath);
    if ((cchTemp == 0) || (cchTemp >= sizeof(TempPath))) {
        TempPath[0] = '.';
        TempPath[1] = '\0';
    }

    if(GetTempFileNameA(TempPath,"dc",0,pszTempName)) {
        DeleteFileA(pszTempName);
    }

    return(TRUE);
}


BOOL
DIAMONDAPI
fciNextCabinetCB(
    OUT PCCAB Cabinet,
    IN  DWORD CabinetSizeEstimate,
    IN  PVOID Context
    )

/*++

Routine Description:

    Callback used by diamond to request a new cabinet file.
    This functionality is not used in our implementation as
    we deal only with single-file cabinets.

Arguments:

    Cabinet - cabinet structure to be filled in.

    CabinetSizeEstimate - estimated size of cabinet.

    Context - supplies context information.

Return Value:

    FALSE (failure).

--*/

{
    UNREFERENCED_PARAMETER(Cabinet);
    UNREFERENCED_PARAMETER(CabinetSizeEstimate);
    UNREFERENCED_PARAMETER(Context);

    return(FALSE);
}


BOOL
DIAMONDAPI
fciStatusCB(
    IN UINT  StatusType,
    IN DWORD Count1,
    IN DWORD Count2,
    IN PVOID Context
    )

/*++

Routine Description:

    Callback used by diamond to give status on file compression
    and cabinet operations, etc.

Arguments:

    Status Type - supplies status type.

        0 = statusFile   - compressing block into a folder.
                              Count1 = compressed size
                              Count2 = uncompressed size

        1 = statusFolder - performing AddFilder.
                              Count1 = bytes done
                              Count2 = total bytes

    Context - supplies context info.

Return Value:

    TRUE (success).

--*/

{
    PDIAMOND_INFO context;

    UNREFERENCED_PARAMETER(Count2);

    context = (PDIAMOND_INFO)Context;

    if(StatusType == statusFile) {

        //
        // Track compressed size.
        //
        context->CompressedSize += Count1;
    }

    return(TRUE);
}



FNFCIGETOPENINFO(fciOpenInfoCB)
{
    int h;
    WIN32_FIND_DATAA FindData;
    HANDLE FindHandle;
    PDIAMOND_INFO context;
    FILETIME ftLocal;

    context = pv;

    FindHandle = FindFirstFileA(pszName,&FindData);
    if(FindHandle == INVALID_HANDLE_VALUE) {
        return(-1);
    }
    FindClose(FindHandle);

    context->SourceFileSize = FindData.nFileSizeLow;
    context->SourceFileCreationTime = FindData.ftCreationTime;
    context->SourceFileModifiedTime = FindData.ftLastWriteTime;
    context->SourceFileAccessedTime = FindData.ftLastAccessTime;

    FileTimeToLocalFileTime(&FindData.ftLastWriteTime, &ftLocal);
    FileTimeToDosDateTime(&ftLocal, pdate, ptime);
    *pattribs = (WORD)(FindData.dwFileAttributes &
            ( FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN |
              FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE ));

    h = _open(pszName,_O_RDONLY | _O_BINARY);
    if(h == -1) {
        return(-1);
    }

    return(h);
}


FNFCIOPEN(fciOpen)
{
    int result;

    result = _open(pszFile, oflag, pmode);

    if (result == -1) {
        *err = errno;
    }

    return(result);
}

FNFCIREAD(fciRead)
{
    UINT result;

    result = (UINT) _read((HFILE)hf, memory, cb);

    if (result != cb) {
        *err = errno;
    }

    return(result);
}

FNFCIWRITE(fciWrite)
{
    UINT result;

    result = (UINT) _write((HFILE)hf, memory, cb);

    if (result != cb) {
        *err = errno;
    }

    return(result);
}

FNFCICLOSE(fciClose)
{
    int result;

    result = _close((HFILE)hf);

    if (result == -1) {
        *err = errno;
    }

    return(result);
}

FNFCISEEK(fciSeek)
{
    long result;

    result = _lseek((HFILE)hf, dist, seektype);

    if (result == -1) {
        *err = errno;
    }

    return(result);

}

FNFCIDELETE(fciDelete)
{
    int result;

    result = _unlink(pszFile);

    if (result == -1) {
        *err = errno;
    }

    return(result);
}


INT
DiamondCompressFile(
    IN  NOTIFYPROC CompressNotify,
    IN  PSTR       SourceFile,
    IN  PSTR       TargetFile,
    IN  BOOL       Rename,
    OUT PLZINFO    pLZI
    )
{
    BOOL b;
    PSTR SourceFilenamePart,p;
    HFCI FciContext;
    ERF  FciError;
    CCAB ccab;
    CHAR targetFile[MAX_PATH];
    DIAMOND_INFO Context;
    INT Status;

    __try {

        //
        // Isolate the filename part of the source file.
        //
        if(SourceFilenamePart = strrchr(SourceFile,'\\')) {
            SourceFilenamePart++;
        } else {
            SourceFilenamePart = SourceFile;
        }
    
        //
        // Form the actual name of the target file.
        //
        lstrcpy(targetFile,TargetFile);
        if(Rename) {
            MakeCompressedName(targetFile);
        }
    
        //
        // Fill in the cabinet structure.
        //
        ZeroMemory(&ccab,sizeof(ccab));
    
        lstrcpyA(ccab.szCabPath,targetFile);
        if(p=strrchr(ccab.szCabPath,'\\')) {
            lstrcpyA(ccab.szCab,++p);
            *p = 0;
        } else {
            lstrcpyA(ccab.szCab,targetFile);
            ccab.szCabPath[0] = 0;
        }
    
        //
        // Call the notification function to see whether we are really
        // supposed to compress this file.
        //
        if(!CompressNotify(SourceFile,targetFile,NOTIFY_START_COMPRESS)) {
            Status = BLANK_ERROR;
            __leave;
        }
    
        ZeroMemory(&Context,sizeof(Context));
    
        //
        // Compress the file.
        //
        FciContext = FCICreate(
                        &FciError,
                        fciFilePlacedCB,
                        fciAllocCB,
                        fciFreeCB,
                        fciOpen,
                        fciRead,
                        fciWrite,
                        fciClose,
                        fciSeek,
                        fciDelete,
                        fciTempFileCB,
                        &ccab,
                        &Context
                        );
    
        if(FciContext) {
    
            b = FCIAddFile(
                    FciContext,
                    SourceFile,         // file to add to cabinet.
                    SourceFilenamePart, // filename part, name to store in cabinet.
                    FALSE,
                    fciNextCabinetCB,   // routine for next cabinet (always fails)
                    fciStatusCB,
                    fciOpenInfoCB,
                    DiamondCompressionType
                    );
    
            if(b) {
    
                b = FCIFlushCabinet(
                        FciContext,
                        FALSE,
                        fciNextCabinetCB,
                        fciStatusCB
                        );
    
                if(b) {
    
                    HANDLE FindHandle;
                    WIN32_FIND_DATA FindData;
    
                    //
                    // Context.CompressedSize does not include headers
                    // and any other file overhead.
                    //
                    FindHandle = FindFirstFile(targetFile,&FindData);
                    if(FindHandle == INVALID_HANDLE_VALUE) {
                        pLZI->cblOutSize = (LONG)Context.CompressedSize;
                    } else {
                        pLZI->cblOutSize = (LONG)FindData.nFileSizeLow;
                        FindClose(FindHandle);
                    }
    
                    pLZI->cblInSize = (LONG)Context.SourceFileSize;
    
                    FindHandle = CreateFile(targetFile,
                            GENERIC_READ | GENERIC_WRITE,
                            0,
                            NULL,
                            OPEN_EXISTING,
                            0,
                            NULL);
                    if (FindHandle != INVALID_HANDLE_VALUE)
                    {
                        SetFileTime(FindHandle,
                            &Context.SourceFileCreationTime,
                            &Context.SourceFileAccessedTime,
                            &Context.SourceFileModifiedTime);
    
                        CloseHandle(FindHandle);
                    }
                }
            }
    
            if(b) {
                Status = TRUE;
            } else {
    
                switch(FciError.erfOper) {
    
                case FCIERR_OPEN_SRC:
                    Status = LZERROR_BADINHANDLE;
                    break;
    
                case FCIERR_READ_SRC:
                    Status = LZERROR_READ;
                    break;
    
                case FCIERR_CAB_FILE:
                    Status = LZERROR_WRITE;
                    break;
    
                case FCIERR_ALLOC_FAIL:
                    Status = LZERROR_GLOBALLOC;
                    break;
    
                case FCIERR_TEMP_FILE:
                case FCIERR_BAD_COMPR_TYPE:
                case FCIERR_USER_ABORT:
                case FCIERR_MCI_FAIL:
                default:
                    Status = FALSE;
                }
            }
    
            FCIDestroy(FciContext);
        } else {
            Status = LZERROR_GLOBALLOC;
        }
    } __except(EXCEPTION_EXECUTE_HANDLER) {
          Status = GetLastError();
    }


    return(Status);
}