#include "precomp.h"
#pragma hdrstop

DWORD
pOpenAndMapFile (
    IN PVOID FileName,
    IN BOOL NameIsUnicode,
    IN DWORD DesiredAccess,
    OUT PHANDLE FileHandle,
    OUT PLARGE_INTEGER Size,
    OUT PHANDLE MappingHandle,
    OUT PVOID *MappedBase
    )
{
    BOOL ok;
    DWORD error;
    LARGE_INTEGER Zero;

    *MappingHandle = NULL;
    *MappedBase = NULL;

    if ( NameIsUnicode ) {
        *FileHandle = CreateFile(
                        FileName,
                        DesiredAccess,
                        0,
                        NULL,
                        OPEN_EXISTING,
                        0,
                        NULL
                        );
    } else {
        *FileHandle = CreateFileA(
                        FileName,
                        DesiredAccess,
                        0,
                        NULL,
                        OPEN_EXISTING,
                        0,
                        NULL
                        );
    }
    if ( *FileHandle == INVALID_HANDLE_VALUE ) {
        error = GetLastError( );
        goto cleanup;
    }

    Zero.QuadPart = 0;
    ok = SetFilePointerEx( *FileHandle, Zero, Size, FILE_END );
    if ( !ok ) {
        error = GetLastError( );
        goto cleanup;
    }
    if ( Size->QuadPart == 0 ) {
        error = NO_ERROR;
        goto cleanup;
    }

    *MappingHandle = CreateFileMapping(
                        *FileHandle,
                        NULL,
                        (DesiredAccess == GENERIC_READ) ? PAGE_READONLY : PAGE_READWRITE,
                        Size->HighPart,
                        Size->LowPart,
                        NULL
                        );
    if ( *MappingHandle == NULL ) {
        error = GetLastError( );
        goto cleanup;
    }

    *MappedBase = MapViewOfFile(
                    *MappingHandle,
                    (DesiredAccess == GENERIC_READ) ? FILE_MAP_READ : (FILE_MAP_READ | FILE_MAP_WRITE),
                    0,
                    0,
                    Size->LowPart
                    );
    if ( *MappedBase == NULL ) {
        error = GetLastError( );
        goto cleanup;
    }

    return NO_ERROR;

cleanup:

    if ( *MappedBase != NULL ) {
        UnmapViewOfFile( *MappedBase );
        *MappedBase = NULL;
    }
    if ( *MappingHandle != NULL ) {
        CloseHandle( *MappingHandle );
        *MappingHandle = NULL;
    }
    if ( *FileHandle != INVALID_HANDLE_VALUE ) {
        CloseHandle( *FileHandle );
        *FileHandle = INVALID_HANDLE_VALUE;
    }

    return error;
}

DWORD
OpenAndMapFile (
    IN PWCH FileName,
    IN DWORD DesiredAccess,
    OUT PHANDLE FileHandle,
    OUT PLARGE_INTEGER Size,
    OUT PHANDLE MappingHandle,
    OUT PVOID *MappedBase
    )
{
    return pOpenAndMapFile(
                FileName,
                TRUE,
                DesiredAccess,
                FileHandle,
                Size,
                MappingHandle,
                MappedBase
                );
}

DWORD
OpenAndMapFileA (
    IN PSZ FileName,
    IN DWORD DesiredAccess,
    OUT PHANDLE FileHandle,
    OUT PLARGE_INTEGER Size,
    OUT PHANDLE MappingHandle,
    OUT PVOID *MappedBase
    )
{
    return pOpenAndMapFile(
                FileName,
                FALSE,
                DesiredAccess,
                FileHandle,
                Size,
                MappingHandle,
                MappedBase
                );
}

VOID
CloseMappedFile (
    IN HANDLE FileHandle,
    IN HANDLE MappingHandle,
    IN PVOID MappedBase
    )
{
    if ( FileHandle != INVALID_HANDLE_VALUE ) {
        if ( MappedBase != NULL ) {
            UnmapViewOfFile( MappedBase );
        }
        if ( MappingHandle != NULL ) {
            CloseHandle( MappingHandle );
        }
        CloseHandle( FileHandle );
    }

    return;
}

DWORD
DataLooksBinary (
    IN PVOID MappedBase,
    IN DWORD FileSize,
    OUT PUCHAR BinaryData OPTIONAL,
    OUT PDWORD BinaryDataOffset OPTIONAL
    )
{
    DWORD nBytes;
    DWORD nBinary;
    DWORD offset;
    DWORD i;
    PUCHAR p;
    UCHAR c;
    DWORD previousBinary;
    BOOL anyBinaryFound;

    if ( IsTextUnicode( MappedBase, FileSize < 512 ? FileSize : 512, NULL ) ) {
        return SCAN_FILETYPE_UNICODE_TEXT;
    }

    anyBinaryFound = FALSE;

    for ( offset = 0;
          offset < FileSize;
          offset += nBytes ) {
    
        previousBinary = MAXULONG;
        nBinary = 0;

        nBytes = 512;
        if ( offset + nBytes > FileSize ) {
            nBytes = FileSize - offset;
        }

        for ( i = 0, p = (PUCHAR)MappedBase + offset;
              i < nBytes;
              i++, p++ ) {
    
            c = *p;
            switch ( c ) {
            
            case '\a':  // ignore all BELL for this test
            case '\b':  //            BS
            case '\f':  //            FF
            case '\n':  //            LF
            case '\r':  //            CR
            case '\t':  //            tab
            case '\v':  //            VT
            case 0x1A:  //            ^Z
                break;
    
            default:
                if ( (c < ' ') || (c > '~') ) {   // worry about DBCS?
                    nBinary++;
                    if ( previousBinary == MAXULONG ) {
                        if ( ARGUMENT_PRESENT(BinaryData) ) {
                            *BinaryData = c;
                        }
                        if ( ARGUMENT_PRESENT(BinaryDataOffset) ) {
                            *BinaryDataOffset = offset + i;
                        }
                        previousBinary = c;
                    }
                }
                break;
            }
        }

        if ( nBinary != 0 ) {
            anyBinaryFound = TRUE;
            if ( nBinary > (nBytes / 5) ) {
                return SCAN_FILETYPE_BINARY;
            }
        }
    }

    if ( anyBinaryFound ) {
        return SCAN_FILETYPE_MAYBE_BINARY;
    }
    return SCAN_FILETYPE_TEXT;

} // DataLooksBinary

DWORD
FileLooksBinary (
    PWCH DirectoryName,
    PWCH FileName,
    OUT PUCHAR BinaryData OPTIONAL,
    OUT PDWORD BinaryDataOffset OPTIONAL
    )
{
    DWORD error;
    LARGE_INTEGER size;
    HANDLE fileHandle;
    HANDLE mappingHandle;
    PVOID mappedBase;
    WCHAR fullName[MAX_PATH+1];
    DWORD i;

    wcscpy( fullName, DirectoryName );
    wcscat( fullName, L"\\" );
    wcscat( fullName, FileName );

    error = OpenAndMapFile (
                fullName,
                GENERIC_READ,
                &fileHandle,
                &size,
                &mappingHandle,
                &mappedBase
                );
    if ( error != NO_ERROR ) {
        return SCAN_FILETYPE_TEXT;
    }

    if ( size.QuadPart == 0 ) {
        return SCAN_FILETYPE_TEXT;
    }

    i = DataLooksBinary(
            mappedBase,
            size.HighPart == 0 ? size.LowPart : MAXULONG,
            BinaryData,
            BinaryDataOffset
            );

    CloseMappedFile( fileHandle, mappingHandle, mappedBase );

    return i;
}