////////////////////////////////////////////////////////////////////
// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
// All rights reserved
// This file was released under the GPLv2 on June 2015.
////////////////////////////////////////////////////////////////////

#ifndef UDF_FORMAT_MEDIA
ULONG   LockMode       = 0;
BOOLEAN opt_invalidate_volume = FALSE;
#endif //UDF_FORMAT_MEDIA

#ifndef CDRW_W32
#ifndef UDF_FORMAT_MEDIA
BOOLEAN open_as_device = FALSE;
#endif //UDF_FORMAT_MEDIA
#ifdef USE_SKIN_MODEL

PSKIN_API SkinAPI = NULL;

PSKIN_API
SkinLoad(
    PWCHAR path,
    HINSTANCE hInstance,      // handle to current instance
    HINSTANCE hPrevInstance,  // handle to previous instance
    int nCmdShow              // show state
    )
{
    HMODULE hm;
    PSKIN_API Skin;
    PSKIN_API (__stdcall *SkinInit) (VOID);

    hm = LoadLibraryW(path);
    if(!hm)
        return NULL;
    SkinInit = (PSKIN_API(__stdcall *)(void))GetProcAddress(hm, "SkinInit");
    if(!SkinInit)
        return NULL;
    Skin = SkinInit();
    if(!Skin)
        return NULL;
    Skin->Init(hInstance, hPrevInstance, nCmdShow);
    return Skin;
}


#endif //USE_SKIN_MODEL

#ifdef _BROWSE_UDF_
#ifndef LIBUDF

extern PVCB Vcb;

#endif // LIBUDF
#endif //_BROWSE_UDF_

#ifdef LIBUDF
#define _lphUdf  ((PUDF_VOL_HANDLE_I)(DeviceObject->lpContext))
#endif //LIBUDF
#ifdef LIBUDFFMT
#define _lphUdf  (DeviceObject->cbio)
#endif //LIBUDFFMT

#ifndef CDRW_W32

NTSTATUS
UDFPhSendIOCTL(
    IN ULONG IoControlCode,
    IN PDEVICE_OBJECT DeviceObject,
    IN PVOID InputBuffer ,
    IN ULONG InputBufferLength,
    OUT PVOID OutputBuffer ,
    IN ULONG OutputBufferLength,
    IN BOOLEAN OverrideVerify,
    OUT PVOID Iosb OPTIONAL
    )
{
    ULONG real_read;
#if !defined(LIBUDF) && !defined(LIBUDFFMT)
    ULONG ret;

    ULONG RC = DeviceIoControl(DeviceObject->h,IoControlCode,
                                InputBuffer,InputBufferLength,
                                OutputBuffer,OutputBufferLength,
                                &real_read,NULL);

    if (!RC) {
        ret = GetLastError();
    }
    return RC ? 1 : -1;

#else // LIBUDF

    ULONG RC = _lphUdf->lpIOCtlFunc(_lphUdf->lpParameter,IoControlCode,
                                InputBuffer,InputBufferLength,
                                OutputBuffer,OutputBufferLength,
                                &real_read);

    return RC;

#endif // LIBUDF

} // end UDFPhSendIOCTL()


NTSTATUS
UDFPhReadSynchronous(
    PDEVICE_OBJECT DeviceObject,  // the physical device object
    PVOID          Buffer,
    ULONG          Length,
    LONGLONG       Offset,
    PULONG         ReadBytes,
    ULONG          Flags
    )
{

#if !defined(LIBUDF) && !defined(LIBUDFFMT)

    NTSTATUS    RC;
//    UDFPrint(("UDFPhRead: Length: %x Lba: %lx\n",Length>>0xb,Offset>>0xb));
    LONG HiOffs = (ULONG)(Offset >> 32);

    RC = SetFilePointer(DeviceObject->h,(ULONG)Offset,&HiOffs,FILE_BEGIN);
    if(RC == INVALID_SET_FILE_POINTER) {
        if(GetLastError() != NO_ERROR) {
            UDFPrint(("UDFPhReadSynchronous: error %x\n", GetLastError()));
            return STATUS_END_OF_FILE;
        }
    }
    RC = ReadFile(DeviceObject->h,Buffer,Length,ReadBytes,NULL);
    if(NT_SUCCESS(RC) &&
        (!(*ReadBytes))) {
        RC = GetLastError();
        return STATUS_END_OF_FILE;
    }
    return STATUS_SUCCESS;

#else // LIBUDF

    return _lphUdf->lpReadFunc(_lphUdf->lpParameter,
                              Buffer,
                              Length,
                              Offset,
                              ReadBytes);

#endif //defined LIBUDF || defined LIBUDFFMT

} // end UDFPhReadSynchronous()


NTSTATUS
UDFPhWriteSynchronous(
    PDEVICE_OBJECT     DeviceObject,  // the physical device object
    PVOID          Buffer,
    ULONG          Length,
    LONGLONG       Offset,
    PULONG         WrittenBytes,
    ULONG          Flags
    )
{
#if !defined(LIBUDF) && !defined(LIBUDFFMT)

    NTSTATUS    RC = STATUS_SUCCESS;
    LONG HiOffs = (ULONG)(Offset >> 32);
    PVOID Buffer2 = NULL;
    PVOID Buffer3 = NULL;

    RC = SetFilePointer(DeviceObject->h,(ULONG)Offset,&HiOffs,FILE_BEGIN);
    if(RC == INVALID_SET_FILE_POINTER) {
        if(GetLastError() != NO_ERROR) {
            UDFPrint(("UDFPhWriteSynchronous: error %x\n", GetLastError()));
            return STATUS_END_OF_FILE;
        }
    }

    Buffer2 = ExAllocatePool(NonPagedPool, Length+0x10000);
    Buffer3 = (PVOID)( ((ULONG)Buffer2 + 0xffff) & ~0xffff);
    RtlCopyMemory(Buffer3, Buffer, Length);

    RC = WriteFile(DeviceObject->h,Buffer3,Length,WrittenBytes,NULL);
    if(!RC ||
        !(*WrittenBytes)) {
        RC = GetLastError();
        UDFPrint(("UDFPhWriteSynchronous: EOF, error %x\n", RC));
        RC = STATUS_END_OF_FILE;
    } else {
        RC = STATUS_SUCCESS;
    }

    if(Buffer2) ExFreePool(Buffer2);

    return RC;

#else // LIBUDF

    return _lphUdf->lpWriteFunc(_lphUdf->lpParameter,
                              Buffer,
                              Length,
                              Offset,
                              WrittenBytes);

#endif // LIBUDF

} // end UDFPhWriteSynchronous()

#if 0
NTSTATUS
UDFPhWriteVerifySynchronous(
    PDEVICE_OBJECT  DeviceObject,   // the physical device object
    PVOID           Buffer,
    ULONG           Length,
    LONGLONG        Offset,
    PULONG          WrittenBytes,
    ULONG           Flags
    )
{
    NTSTATUS RC;
    PUCHAR v_buff = NULL;
    ULONG ReadBytes;

    RC = UDFPhWriteSynchronous(DeviceObject, Buffer, Length, Offset, WrittenBytes, 0);
    if(!Verify)
        return RC;
    v_buff = (PUCHAR)DbgAllocatePool(NonPagedPool, Length);
    if(!v_buff)
        return RC;

    RC = UDFPhSendIOCTL( IOCTL_CDRW_SYNC_CACHE, DeviceObject,
                    NULL,0, NULL,0, FALSE, NULL);

    RC = UDFPhReadSynchronous(DeviceObject, v_buff, Length, Offset, &ReadBytes, 0);
    if(!NT_SUCCESS(RC)) {
        BrutePoint();
        DbgFreePool(v_buff);
        return RC;
    }
    if(RtlCompareMemory(v_buff, Buffer, ReadBytes) == Length) {
        DbgFreePool(v_buff);
        return RC;
    }
    BrutePoint();
    DbgFreePool(v_buff);
    return STATUS_LOST_WRITEBEHIND_DATA;
} // end UDFPhWriteVerifySynchronous()
#endif

VOID
set_image_size(
    HANDLE h,
//    ULONG LBA)
    int64  len)
{
    LONG offh = (ULONG)(len >> 32);
                        //( (LONGLONG)LBA >> (32-Vcb->BlockSizeBits) );

    SetFilePointer((HANDLE)h, (ULONG)(len /*(LBA << Vcb->BlockSizeBits)*/ ), &offh, FILE_BEGIN);
    SetEndOfFile(h);
    offh = 0;
    SetFilePointer((HANDLE)h, 0, &offh, FILE_BEGIN);
} // end set_image_size()

int64
get_file_size(
    HANDLE h
    )
{
    LONG hsz = 0;
    LONG lsz;

    lsz = SetFilePointer(h, 0, &hsz, FILE_END);
    return (((int64)hsz) << 32) | lsz;
} // end get_file_size()

int64
set_file_pointer(
    HANDLE h,
    int64 sz
    )
{
    ULONG hsz = (ULONG)(sz >> 32);
    ULONG lsz = (ULONG)sz;

    lsz = SetFilePointer(h, lsz, (PLONG)&hsz, FILE_BEGIN);
    return (((int64)hsz) << 32) | lsz;
} // end set_file_pointer()

#endif //CDRW_W32

#ifndef LIBUDF

#ifndef UDF_FORMAT_MEDIA

ULONG
write(
    PVCB Vcb,
    HANDLE h,
    PCHAR buff,
    ULONG len)
{
    ULONG written;
    LONG offh = 0;
    ULONG offl = SetFilePointer((HANDLE)h, 0, &offh, FILE_CURRENT);
//    ULONG Lba = (ULONG)((((LONGLONG)offh << 32) + offl) >> Vcb->BlockSizeBits);

    UDFWriteData(Vcb, FALSE, (((LONGLONG)offh)<<32)+offl, len, FALSE, buff, &written);

    SetFilePointer((HANDLE)h, offl, &offh, FILE_BEGIN);
    offh = 0;
    SetFilePointer((HANDLE)h, written, &offh, FILE_CURRENT);

    return written;
} // end write()
#endif //UDF_FORMAT_MEDIA

#endif // LIBUDF

#endif //CDRW_W32

#ifdef NT_NATIVE_MODE

BOOL
Privilege(
    LPTSTR pszPrivilege, 
    BOOL bEnable
    )
{
#ifndef NT_NATIVE_MODE
    HANDLE           hToken;
    TOKEN_PRIVILEGES tp;

    // obtain the token, first check the thread and then the process
    if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, TRUE, &hToken)) {
        if (GetLastError() == ERROR_NO_TOKEN) {
            if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
                return FALSE;
            }
        } else {
            return FALSE;
        }
    }

    // get the luid for the privilege
    if (!LookupPrivilegeValue(NULL, pszPrivilege, &tp.Privileges[0].Luid)) {
        CloseHandle(hToken);
        return FALSE;
    }

    tp.PrivilegeCount = 1;

    if (bEnable)
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // enable or disable the privilege
    if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, (PTOKEN_PRIVILEGES)NULL, 0)) {
        CloseHandle(hToken);
        return FALSE;
    }

    if (!CloseHandle(hToken))
        return FALSE;

#endif //NT_NATIVE_MODE

    return TRUE;

} // end Privilege()
#endif //NT_NATIVE_MODE

#ifndef LIBUDF

extern "C"
ULONG
MyLockVolume(
    HANDLE h,
    ULONG* pLockMode // OUT
    )
{
    ULONG RC;
    ULONG returned;

    (*pLockMode) = -1;
#ifndef CDRW_W32
    RC = DeviceIoControl(h,IOCTL_UDF_LOCK_VOLUME_BY_PID,NULL,0,NULL,0,&returned,NULL);
    if(RC) {
        (*pLockMode) = IOCTL_UDF_LOCK_VOLUME_BY_PID;
        return STATUS_SUCCESS;
    }
#endif //CDRW_W32

    RC = DeviceIoControl(h,FSCTL_LOCK_VOLUME,NULL,0,NULL,0,&returned,NULL);
    if(RC) {
        (*pLockMode) = FSCTL_LOCK_VOLUME;
        return STATUS_SUCCESS;
    }
    return STATUS_UNSUCCESSFUL;
} // MyLockVolume()

extern "C"
ULONG
MyUnlockVolume(
    HANDLE h,
    ULONG* pLockMode // IN
    )
{
    ULONG returned;

#ifndef CDRW_W32
    if((*pLockMode) == IOCTL_UDF_LOCK_VOLUME_BY_PID) {
        return DeviceIoControl(h,IOCTL_UDF_UNLOCK_VOLUME_BY_PID,NULL,0,NULL,0,&returned,NULL);
    }
#endif //CDRW_W32

    return DeviceIoControl(h,FSCTL_UNLOCK_VOLUME,NULL,0,NULL,0,&returned,NULL);

} // MyUnlockVolume()

void
my_retrieve_vol_type(
#ifndef CDRW_W32
    PVCB Vcb,
#endif
    PWCHAR fn
    )
{
#ifndef CDRW_W32
    if(wcslen(fn) == 2 && fn[1] == ':') {
        ULONG DevType = GetDriveTypeW(fn);
        UDFPrint(("  DevType %x\n", DevType));
        switch(DevType) {
        case DRIVE_CDROM:
            Vcb->PhDeviceType = FILE_DEVICE_CD_ROM;
            break;
        default:
            Vcb->PhDeviceType = FILE_DEVICE_DISK;
            break;
        }
    }
    if(wcslen(fn) == 2 && fn[1] == ';') {
        UserPrint(("Warrning: File name is similar to drive letter.\n"
                   "  Don't you type semicolon ';' instead of colon ':' ?\n"));
    }
#endif //CDRW_W32
} // end my_retrieve_vol_type()


#ifdef NT_NATIVE_MODE
#define GetLastError()    ((ULONG)(-1))
#endif //NT_NATIVE_MODE

#define MAX_INVALIDATE_VOLUME_RETRY 8

extern "C"
HANDLE
my_open(
#ifndef CDRW_W32
    PVCB Vcb,
#endif
    PWCHAR fn
    )
{
    HANDLE h/*, h2*/;
    WCHAR deviceNameBuffer[0x200];
    WCHAR FSNameBuffer[0x200];
//    CCHAR RealDeviceName[0x200];
//    WCHAR DeviceName[MAX_PATH+1];
    ULONG RC;
    ULONG retry;
    ULONG i;
    BOOLEAN CantLock = FALSE;
    PULONG pLockMode;
#ifdef NT_NATIVE_MODE
    IO_STATUS_BLOCK ioStatus;
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING uniFilename;
#endif //NT_NATIVE_MODE
    ULONG returned;

#ifndef CDRW_W32
#ifdef UDF_FORMAT_MEDIA
    PUDFFmtState fms = Vcb->fms;
    fms->
#endif
        open_as_device = TRUE;
#endif //CDRW_W32

    pLockMode = &
#ifdef UDF_FORMAT_MEDIA
        fms->
#endif
        LockMode;

    // make several retries to workaround smart applications,
    // those attempts to work with volume immediately after arrival
    retry = 1 +
#ifdef UDF_FORMAT_MEDIA
        fms->
#endif
            opt_invalidate_volume ? 0 : MAX_INVALIDATE_VOLUME_RETRY;

#ifndef NT_NATIVE_MODE
    swprintf(deviceNameBuffer, L"%ws\\", fn);
    UDFPrint(("my_open: %S\n", fn));
    i = sizeof(FSNameBuffer)/sizeof(FSNameBuffer[0]);
    if(GetVolumeInformationW(deviceNameBuffer, NULL, 0, 
        &returned, &returned, &returned, FSNameBuffer, i)) {
        UDFPrint(("my_open: FS: %S\n", FSNameBuffer));
        if(!wcscmp(FSNameBuffer, L"Unknown")) {
            retry++;
        }
    } else {
        UDFPrint(("my_open: FS: ???\n"));
    }
    UDFPrint(("my_open: retry %d times\n", retry));

#endif //NT_NATIVE_MODE

    do {
    // open as device
#ifndef NT_NATIVE_MODE
    swprintf(deviceNameBuffer, L"\\\\.\\%ws", fn);
    if(wcslen(fn) == 2 && fn[1] == ';') {
        UserPrint(("Warrning: File name is similar to drive letter.\n"
                   "  Don't you type semicolon ';' instead of colon ':' ?\n"));
    }
    h = (HANDLE)(-1);
    for(i=0; i<4; i++) {
        if(h == ((HANDLE)-1)) {
            h = CreateFileW(deviceNameBuffer, GENERIC_READ | GENERIC_WRITE,
                           ((i & 1) ? 0 : FILE_SHARE_READ) | ((i & 2) ? 0 : FILE_SHARE_WRITE),
                           NULL,
                           OPEN_EXISTING,
                           FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING,  NULL);
            if(h != ((HANDLE)-1)) {
                UDFPrint(("  opened i=%x\n", i));
            }
        }
    }
#else //NT_NATIVE_MODE
    uniFilename.Length = swprintf(deviceNameBuffer, L"\\??\\%ws", fn);
    uniFilename.Buffer = deviceNameBuffer;
    uniFilename.Length *= sizeof(WCHAR);
    uniFilename.MaximumLength = uniFilename.Length + sizeof(WCHAR);

    h = (HANDLE)(-1);
    for(i=0; i<4; i++) {
        InitializeObjectAttributes(&ObjectAttributes, &uniFilename, OBJ_CASE_INSENSITIVE, NULL, NULL);
        if(h == ((HANDLE)-1)) {
            RC = NtCreateFile(&h,
                                     GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
                                     &ObjectAttributes,
                                     &ioStatus,
                                     NULL,
                                     FILE_ATTRIBUTE_NORMAL,
                                     ((i & 1) ? 0 : FILE_SHARE_READ) | ((i & 2) ? 0 : FILE_SHARE_WRITE),
                                     FILE_OPEN,
                                     FILE_SYNCHRONOUS_IO_NONALERT | FILE_COMPLETE_IF_OPLOCKED | FILE_WRITE_THROUGH | FILE_NO_INTERMEDIATE_BUFFERING,
                                     NULL,
                                     0);
            if(!NT_SUCCESS(RC)) {
                UDFPrint(("  opened i2=%x\n", i));
                h = ((HANDLE)-1);
            }
        }
    }
#endif //NT_NATIVE_MODE
    if(h != ((HANDLE)-1)) {
#ifndef CDRW_W32
#ifdef UDF_FORMAT_MEDIA
        if(fms->opt_flush || fms->opt_probe) {
            return h;
        }
#endif //UDF_FORMAT_MEDIA
        my_retrieve_vol_type(Vcb, fn);
#else
        my_retrieve_vol_type(fn);
#endif //CDRW_W32
        if(!NT_SUCCESS(MyLockVolume(h,pLockMode))) {
#ifndef CDRW_W32
            if(retry < MAX_INVALIDATE_VOLUME_RETRY) {
                retry++;
                if(!Privilege(SE_TCB_NAME, TRUE)) {
                    UDFPrint(("SE_TCB privilege not held\n"));
                } else
                if(DeviceIoControl(h,FSCTL_INVALIDATE_VOLUMES,&h,sizeof(h),NULL,0,&returned,NULL) ) {
                    UDFPrint(("  FSCTL_INVALIDATE_VOLUMES ok, status %x\n", GetLastError()));
                    CloseHandle(h);
                    continue;
                } else {
//#ifndef CDRW_W32
                    UDFPrint(("  FSCTL_INVALIDATE_VOLUMES failed, error %x\n", GetLastError()));
                    RC = GetLastError();
                    if(DeviceIoControl(h,IOCTL_UDF_INVALIDATE_VOLUMES,&h,sizeof(h),NULL,0,&returned,NULL) ) {
                        UDFPrint(("  IOCTL_UDF_INVALIDATE_VOLUMES ok, status %x\n", GetLastError()));
                        CloseHandle(h);
                        continue;
                    }
                    UDFPrint(("  IOCTL_UDF_INVALIDATE_VOLUMES, error %x\n", GetLastError()));
//#endif //CDRW_W32
                }
                UserPrint(("can't lock volume, retry\n"));
                CloseHandle(h);
                continue;
            }
#endif //CDRW_W32
            UserPrint(("can't lock volume\n"));
#ifndef NT_NATIVE_MODE
            // In native mode the volume can be not mounted yet !!!
            CantLock = TRUE;
            CloseHandle(h);
            h = NULL;
            goto try_as_file;
#endif //NT_NATIVE_MODE
        }
//#ifndef CDRW_W32
        if(!DeviceIoControl(h,FSCTL_ALLOW_EXTENDED_DASD_IO,NULL,0,NULL,0,&returned,NULL)) {
            UDFPrint(("Warning: can't allow extended DASD i/o\n"));
        }
//#endif //CDRW_W32

        UDFPrint(("  opened, h=%x\n", h));
        return h;
    }
    RC = GetLastError();

#ifndef NT_NATIVE_MODE
    h = CreateFileW(deviceNameBuffer, GENERIC_READ,
                   FILE_SHARE_READ,
                   NULL,
                   OPEN_EXISTING,
                   FILE_ATTRIBUTE_NORMAL,  NULL);
#else //NT_NATIVE_MODE
    RC = NtCreateFile(&h,
                             GENERIC_READ | SYNCHRONIZE | FILE_READ_ATTRIBUTES,
                             &ObjectAttributes,
                             &ioStatus,
                             NULL,
                             FILE_ATTRIBUTE_NORMAL,
                             FILE_SHARE_READ,
                             FILE_OPEN,
                             FILE_SYNCHRONOUS_IO_NONALERT | FILE_COMPLETE_IF_OPLOCKED | FILE_WRITE_THROUGH,
                             NULL,
                             0);
    if(!NT_SUCCESS(RC)) {
        h = ((HANDLE)-1);
    }
#endif //NT_NATIVE_MODE
    if(h != ((HANDLE)-1)) {

        UDFPrint(("  opened R/O, h=%x\n", h));
#ifndef CDRW_W32
        my_retrieve_vol_type(Vcb, fn);
#else
        my_retrieve_vol_type(fn);
#endif

        UserPrint(("read-only open\n"));
        if(!NT_SUCCESS(MyLockVolume(h,pLockMode))) {
#ifndef CDRW_W32
            if(retry < MAX_INVALIDATE_VOLUME_RETRY) {
                retry++;
                if(!Privilege(SE_TCB_NAME, TRUE)) {
                    UDFPrint(("SE_TCB privilege not held\n"));
                } else
                if(DeviceIoControl(h,FSCTL_INVALIDATE_VOLUMES,&h,sizeof(h),NULL,0,&returned,NULL) ) {
                    CloseHandle(h);
                    continue;
                }
                UserPrint(("can't lock read-only volumem retry"));
                CloseHandle(h);
                continue;
            }
#endif //CDRW_W32
            UserPrint(("can't lock read-only volume"));
#ifndef NT_NATIVE_MODE
            CantLock = TRUE;
            CloseHandle(h);
            h = NULL;
            goto try_as_file;
#endif //NT_NATIVE_MODE
        }
//        write_cdfs = TRUE;
//        DeviceIoControl(h,FSCTL_DISMOUNT_VOLUME,NULL,0,NULL,0,&returned,NULL);
        return h;
    }
#ifndef NT_NATIVE_MODE
try_as_file:
#endif //NT_NATIVE_MODE

#ifndef CDRW_W32
#ifdef UDF_FORMAT_MEDIA
    fms->
#endif
    open_as_device = FALSE;
    // open as plain file
    Vcb->PhDeviceType = FILE_DEVICE_DISK;
#endif //CDRW_W32

    UserPrint(("try image file\n"));
#ifndef NT_NATIVE_MODE
    h = CreateFileW(fn, GENERIC_READ | GENERIC_WRITE,
                   FILE_SHARE_READ,
                   NULL,
                   CREATE_ALWAYS,
                   FILE_ATTRIBUTE_NORMAL,  NULL);
#else //NT_NATIVE_MODE
    RC = NtCreateFile(&h,
                             GENERIC_READ | SYNCHRONIZE,
                             &ObjectAttributes,
                             &ioStatus,
                             NULL,
                             FILE_ATTRIBUTE_NORMAL,
                             FILE_SHARE_READ,
                             FILE_OPEN,
                             FILE_SYNCHRONOUS_IO_NONALERT | FILE_COMPLETE_IF_OPLOCKED | FILE_WRITE_THROUGH,
                             NULL,
                             0);
    if(!NT_SUCCESS(RC)) {
        h = ((HANDLE)-1);
    }
#endif //NT_NATIVE_MODE
    if(h == ((HANDLE)-1)) {

        RC = GetLastError();
        if(CantLock) {
#ifndef CDRW_W32
            my_exit(
#ifdef UDF_FORMAT_MEDIA
                fms,
#endif
                MKUDF_CANT_LOCK_VOL);
#else
            return NULL;
#endif //CDRW_W32
        }
#ifndef CDRW_W32
        UserPrint(("error opening device or image file"));
        my_exit(
#ifdef UDF_FORMAT_MEDIA
                fms,
#endif
            MKUDF_CANT_OPEN_FILE);
#else
        return NULL;
#endif //CDRW_W32
    }
    UDFPrint(("  opened as file, h=%x\n", h));
    break;

    } while(TRUE);
    return h;
} // end my_open()

#endif //LIBUDF

#ifndef CDRW_W32

uint64
udf_lseek64(
    HANDLE fd,
    uint64 offset,
    int whence)
{
    LONG offh = (ULONG)(offset>>32);
    LONG offl;
    offl = SetFilePointer(fd, (ULONG)offset, &offh, whence);
    if(offl == -1 && offh == -1) {
        return -1;
    }
    return (((uint64)offh) << 32) | (uint64)offl;
} // end udf_lseek64()

#ifdef LIBUDFFMT
BOOLEAN
udf_get_sizes(
    IN PDEVICE_OBJECT DeviceObject,
    IN ULONG* blocks
    )
{
    ULONG bs;
    int64 sz;
    ULONG RC;

    RC = _lphUdf->lpGetSizeFunc(_lphUdf->lpParameter, &sz, &bs);

    (*blocks) = (ULONG)(sz/bs);

    return(OS_SUCCESS(RC));
}
#endif //LIBUDFFMT
    
#include "string_lib.cpp"

#ifdef _BROWSE_UDF_
#ifndef LIBUDF

ULONG
UDFGetDevType(
    PDEVICE_OBJECT DeviceObject
    )
{
    if(DeviceObject && DeviceObject == Vcb->TargetDeviceObject) {
        return Vcb->PhDeviceType;
    }
    return FILE_DEVICE_DISK;
} // end UDFGetDevType()

#else  // LIBUDF

ULONG
UDFGetDevType(
    PDEVICE_OBJECT DeviceObject
    )
{
#define lphUdf  ((PUDF_VOL_HANDLE_I)(DeviceObject->lpContext))
    return lphUdf->bHddDevice ? FILE_DEVICE_DISK : FILE_DEVICE_CD_ROM;
#undef lphUdf
} // end UDFGetDevType()

#endif // LIBUDF

#endif //_BROWSE_UDF_

#endif //CDRW_W32

#ifndef NT_NATIVE_MODE

#ifdef PRINT_DBG_CONSOLE
CHAR dbg_print_tmp_buff[2048];

BOOLEAN was_enter = TRUE;

extern "C"
VOID
PrintDbgConsole(
    PCHAR DebugMessage,
    ...
    )
{
    int len;
    va_list ap;
    va_start(ap, DebugMessage);

    if(was_enter) {
        strcpy(&dbg_print_tmp_buff[0], JS_DBG_PREFIX);
        len = _vsnprintf(&dbg_print_tmp_buff[sizeof(JS_DBG_PREFIX)-1], 2047-sizeof(JS_DBG_PREFIX), DebugMessage, ap);
    } else {
        len = _vsnprintf(&dbg_print_tmp_buff[0], 2047, DebugMessage, ap);
    }
    dbg_print_tmp_buff[2047] = 0;
    if(len > 0 &&
       (dbg_print_tmp_buff[len-1] == '\n' ||
        dbg_print_tmp_buff[len-1] == '\r') ) {
        was_enter = TRUE;
    } else {
        was_enter = FALSE;
    }

    OutputDebugString(&dbg_print_tmp_buff[0]);

    va_end(ap);

} // end PrintDbgConsole()
#else // PRINT_DBG_CONSOLE
VOID
PrintDbgConsole(
    PCHAR DebugMessage,
    ...
    )
{
} // end ClassDebugPrint()
#endif //PRINT_DBG_CONSOLE

BOOLEAN
RtlTimeFieldsToTime(
    IN PTIME_FIELDS TimeFields,
    IN PLARGE_INTEGER Time
    )
{
    SYSTEMTIME st;

    st.wYear         = TimeFields->Year;
    st.wMonth        = TimeFields->Month;
    st.wDayOfWeek    = 0;
    st.wDay          = TimeFields->Day;
    st.wHour         = TimeFields->Hour;
    st.wMinute       = TimeFields->Minute;
    st.wSecond       = TimeFields->Second;
    st.wMilliseconds = TimeFields->Milliseconds;

    return SystemTimeToFileTime(&st, (PFILETIME)Time);
} // end RtlTimeFieldsToTime()

BOOLEAN
RtlTimeToTimeFields(
    IN PLARGE_INTEGER Time,
    IN PTIME_FIELDS TimeFields
    )
{
    SYSTEMTIME st;
    BOOLEAN retval;

    retval = FileTimeToSystemTime((PFILETIME)Time, &st);

    TimeFields->Year         = st.wYear;
    TimeFields->Month        = st.wMonth;
    TimeFields->Weekday      = st.wDayOfWeek;
    TimeFields->Day          = st.wDay;
    TimeFields->Hour         = st.wHour;
    TimeFields->Minute       = st.wMinute;
    TimeFields->Second       = st.wSecond;
    TimeFields->Milliseconds = st.wMilliseconds;

    return retval;
} // end ()

#endif //NT_NATIVE_MODE

#ifdef USE_THREAD_HEAPS

HANDLE MemLock = NULL;

VOID
ExInitThreadPools()
{
    MemLock = CreateMutex(NULL, 0, NULL);
}

VOID
ExDeInitThreadPools()
{
    if(MemLock)
        CloseHandle(MemLock);
}

#define MAX_THREADS_WITH_OWN_POOL   128

typedef struct _THREAD_POOL_LIST_ITEM {
    HANDLE HeapHandle;
    ULONG  ThreadId;
} THREAD_POOL_LIST_ITEM, *PTHREAD_POOL_LIST_ITEM;

ULONG LastThreadPool = -1;
THREAD_POOL_LIST_ITEM ThreadPoolList[MAX_THREADS_WITH_OWN_POOL];

extern "C"
PVOID
#ifdef KERNEL_MODE_MM_BEHAVIOR
_ExAllocatePool_(
#else
ExAllocatePool(
#endif
    ULONG MemoryType,
    ULONG Size
    )
{
    ULONG i;
    ULONG ThreadId = GetCurrentThreadId();
    BOOLEAN found = FALSE;

    WaitForSingleObject(MemLock,-1);

    for(i=0; i<(LastThreadPool+1); i++) {
        if(ThreadPoolList[i].ThreadId == ThreadId) {
            found = TRUE;
            break;
        }
    }
    if(found) {
        ReleaseMutex(MemLock);
        return HeapAlloc(ThreadPoolList[i].HeapHandle, HEAP_NO_SERIALIZE, Size);
    }
    for(i=0; i<(LastThreadPool+1); i++) {
        if(ThreadPoolList[i].ThreadId == -1) {
            break;
        }
    }
    if(i>=MAX_THREADS_WITH_OWN_POOL) {
        ReleaseMutex(MemLock);
        return NULL;
    }
    ThreadPoolList[i].ThreadId   = ThreadId;
    ThreadPoolList[i].HeapHandle = HeapCreate(HEAP_NO_SERIALIZE, 128*PAGE_SIZE, 0);
    if(!ThreadPoolList[i].HeapHandle) {
        ThreadPoolList[i].ThreadId = -1;
        ReleaseMutex(MemLock);
        return NULL;
    }

    if(i+1 > LastThreadPool+1)
        LastThreadPool = i;

    ReleaseMutex(MemLock);
        
    return HeapAlloc(ThreadPoolList[i].HeapHandle, HEAP_NO_SERIALIZE, Size);

} // end ExAllocatePool()

extern "C"
VOID
#ifdef KERNEL_MODE_MM_BEHAVIOR
_ExFreePool_(
#else
ExFreePool(
#endif
    PVOID Addr
    )
{
    ULONG ThreadId = GetCurrentThreadId();
    ULONG i;

    WaitForSingleObject(MemLock,-1);
    for(i=0; i<(LastThreadPool+1); i++) {
        if(ThreadPoolList[i].ThreadId == ThreadId) {
            break;
        }
    }
    if(i+1 > LastThreadPool+1) {
        // Not found
        BrutePoint();
        //__asm int 3;
        return;
    }
    HeapFree(ThreadPoolList[i].HeapHandle, HEAP_NO_SERIALIZE, Addr);

    ReleaseMutex(MemLock);

} // end ExFreePool()

extern "C"
VOID
ExFreeThreadPool()
{
    ULONG ThreadId = GetCurrentThreadId();
    ULONG i;

    WaitForSingleObject(MemLock,-1);
    for(i=0; i<(LastThreadPool+1); i++) {
        if(ThreadPoolList[i].ThreadId == ThreadId) {
            break;
        }
    }
    if(i+1 > LastThreadPool+1) {
        // Not found
        BrutePoint();
        //__asm int 3;
        return;
    }
    HeapDestroy(ThreadPoolList[i].HeapHandle);
    ThreadPoolList[i].HeapHandle = INVALID_HANDLE_VALUE;
    ThreadPoolList[i].ThreadId   = -1;

    ReleaseMutex(MemLock);
}

#endif //USE_THREAD_HEAPS

#if defined(KERNEL_MODE_MM_BEHAVIOR)
extern "C"
PVOID
ExAllocatePool(
    ULONG MemoryType,
    ULONG Size
    )
{
    PVOID Addr;
    PVOID uAddr;
    if(Size < PAGE_SIZE) {
#ifdef USE_THREAD_HEAPS
        Addr = _ExAllocatePool_(MemoryType, Size+8);
#else
        Addr = GlobalAlloc(GMEM_DISCARDABLE, Size+8);
#endif
        if(!Addr)
            return NULL;
        uAddr = ((PCHAR)Addr)+8;
    } else {
#ifdef USE_THREAD_HEAPS
        Addr = _ExAllocatePool_(MemoryType, Size+PAGE_SIZE*2);
#else
        Addr = GlobalAlloc(GMEM_DISCARDABLE, Size+PAGE_SIZE*2);
#endif
        if(!Addr)
            return NULL;
        uAddr = (PVOID)(((ULONG)(((PCHAR)Addr)+PAGE_SIZE)) & ~(PAGE_SIZE-1));
    }
    *(((PULONG)uAddr)-2) = (ULONG)Addr;
    *(((PULONG)uAddr)-1) = 0xFEDCBA98;
    return uAddr;
} // end ExAllocatePool()

extern "C"
VOID
ExFreePool(
    PVOID uAddr
    )
{
    PVOID Addr;

    if(*(((PULONG)uAddr)-1) == 0xFEDCBA98) {
        Addr = (PVOID)(*(((PULONG)uAddr)-2));
#ifdef USE_THREAD_HEAPS
        _ExFreePool_(Addr);
#else
        GlobalFree(Addr);
#endif
        return;
    }
    BrutePoint();
} // end ExFreePool()
#endif //defined(KERNEL_MODE_MM_BEHAVIOR) || defined(NT_NATIVE_MODE)

#ifdef _lphUdf
#undef _lphUdf
#endif //_lphUdf

extern "C"
BOOLEAN
ProbeMemory(
    PVOID   MemPtr,
    ULONG   Length,
    BOOLEAN ForWrite
    )
{
    ULONG i;
    UCHAR a;
    if(!MemPtr && !Length)
        return TRUE;
    if(!MemPtr || !Length)
        return FALSE;
    _SEH2_TRY {
        a = ((PCHAR)MemPtr)[Length-1];
        if(ForWrite) {
            ((PCHAR)MemPtr)[Length-1] = a;
        }
        for(i=0; i<Length; i+=PAGE_SIZE) {
            a = ((PCHAR)MemPtr)[i];
            if(ForWrite) {
                ((PCHAR)MemPtr)[i] = a;
            }
        }
    } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
        return FALSE;
    } _SEH2_END;
    return TRUE;
} // end ProbeMemory()

#ifdef NT_NATIVE_MODE
#include "env_spec_nt.cpp"
#endif //NT_NATIVE_MODE