////////////////////////////////////////////////////////////////////
// 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.
////////////////////////////////////////////////////////////////////
/*
 Module Name: Phys_lib.cpp

 Execution: Kernel mode only

 Description:

   Contains code that implement read/write operations for physical device
*/

#include "phys_lib.h"

static const char Signature [16] = {CDRW_SIGNATURE_v1};

// Local functions:

OSSTATUS
UDFSetSpeeds(
    IN PVCB Vcb
    );

NTSTATUS
UDFSetCaching(
    IN PVCB Vcb
    );

OSSTATUS
UDFRecoverFromError(
    IN PVCB Vcb,
    IN BOOLEAN WriteOp,
    IN OSSTATUS status,
    IN uint32 Lba,
    IN uint32 BCount,
 IN OUT uint32* retry);

#ifdef _BROWSE_UDF_

uint32
UDFFixFPAddress(
    IN PVCB           Vcb,               // Volume control block from this DevObj
    IN uint32         Lba
    );

#endif //_BROWSE_UDF_

NTSTATUS
UDFSyncCache(
    IN PVCB Vcb
    )
{
    UDFPrint(("UDFSyncCache:\n"));
    OSSTATUS RC;
    RC = UDFPhSendIOCTL( IOCTL_CDRW_SYNC_CACHE, Vcb->TargetDeviceObject,
                    NULL,0, NULL,0, FALSE, NULL);
    if(OS_SUCCESS(RC)) {
        // clear LAST_WRITE flag
        Vcb->VCBFlags &= ~UDF_VCB_LAST_WRITE;
    }
    return RC;
} // end UDFSyncCache()


OSSTATUS
UDFReallocTrackMap(
    IN PVCB Vcb,
    IN uint32 TrackNum
    )
{
#ifdef _BROWSE_UDF_
    if(Vcb->TrackMap) {
        MyFreePool__(Vcb->TrackMap);
        Vcb->TrackMap = NULL;
    }
    Vcb->TrackMap = (PUDFTrackMap)
        MyAllocatePool__(NonPagedPool, TrackNum*sizeof(UDFTrackMap));
    if(!Vcb->TrackMap) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }
#endif //_BROWSE_UDF_
    RtlZeroMemory(Vcb->TrackMap,TrackNum*sizeof(UDFTrackMap));
    return STATUS_SUCCESS;
} // end UDFReallocTrackMap()

#ifdef _BROWSE_UDF_


OSSTATUS
__fastcall
UDFTIOVerify(
    IN void* _Vcb,
    IN void* Buffer,     // Target buffer
    IN SIZE_T Length,
    IN uint32 LBA,
    OUT PSIZE_T IOBytes,
    IN uint32 Flags
    )
{
    OSSTATUS RC = STATUS_SUCCESS;
    uint32 i, j;
    SIZE_T mask;
    uint32 lba0, len, lba1;
    PUCHAR tmp_buff;
    PUCHAR p;
    PCHAR cached_block;
    SIZE_T tmp_wb;
    BOOLEAN need_remap;
    OSSTATUS final_RC = STATUS_SUCCESS;
    BOOLEAN zero;
    BOOLEAN non_zero;
    BOOLEAN packet_ok;
    BOOLEAN free_tmp = FALSE;
    BOOLEAN single_packet = FALSE;

#define Vcb ((PVCB)_Vcb)
    // ATTENTION! Do not touch bad block bitmap here, since it describes PHYSICAL addresses WITHOUT remapping,
    // while here we work with LOGICAL addresses

    if(Vcb->VerifyCtx.ItemCount > UDF_MAX_VERIFY_CACHE) {
        UDFVVerify(Vcb, 0/*UFD_VERIFY_FLAG_WAIT*/);
    }

    UDFAcquireResourceExclusive(&(Vcb->IoResource), TRUE);
    Flags |= PH_IO_LOCKED;

    tmp_wb = (SIZE_T)_Vcb;
    if(Flags & PH_EX_WRITE) {
        UDFPrint(("IO-Write-Verify\n"));
        RC = UDFTWrite(_Vcb, Buffer, Length, LBA, &tmp_wb, Flags | PH_VCB_IN_RETLEN);
    } else {
        UDFPrint(("IO-Read-Verify\n"));
        RC = UDFTRead(_Vcb, Buffer, Length, LBA, &tmp_wb, Flags | PH_VCB_IN_RETLEN);
    }
    (*IOBytes) = tmp_wb;

    switch(RC) {
    default:
        UDFReleaseResource(&(Vcb->IoResource));
        return RC;
    case STATUS_FT_WRITE_RECOVERY:
    case STATUS_DEVICE_DATA_ERROR:
    case STATUS_IO_DEVICE_ERROR:
        break;
        /* FALL THROUGH */
    } // end switch(RC)

    if(!Vcb->SparingCount ||
       !Vcb->SparingCountFree ||
       Vcb->CDR_Mode) {
        UDFPrint(("Can't remap\n"));
        UDFReleaseResource(&(Vcb->IoResource));
        return RC;
    }

    if(Flags & PH_EX_WRITE) {
        UDFPrint(("Write failed, try relocation\n"));
    } else {
        if(Vcb->Modified) {
            UDFPrint(("Read failed, try relocation\n"));
        } else {
            UDFPrint(("no remap on not modified volume\n"));
            UDFReleaseResource(&(Vcb->IoResource));
            return RC;
        }
    }
    if(Flags & PH_LOCK_CACHE) {
        UDFReleaseResource(&(Vcb->IoResource));
        WCacheStartDirect__(&(Vcb->FastCache), Vcb, TRUE);
        UDFAcquireResourceExclusive(&(Vcb->IoResource), TRUE);
    }

    Flags &= ~PH_KEEP_VERIFY_CACHE;

    // NOTE: SparingBlockSize may be not equal to PacketSize
    // perform recovery
    mask = Vcb->SparingBlockSize-1;
    lba0 = LBA & ~mask;
    len = ((LBA+(Length>>Vcb->BlockSizeBits)+mask) & ~mask) - lba0;
    j=0;
    if((lba0 == LBA) && (len == mask+1) && (len == (Length>>Vcb->BlockSizeBits))) {
        single_packet = TRUE;
        tmp_buff = NULL;
    } else {
        tmp_buff = (PUCHAR)DbgAllocatePoolWithTag(NonPagedPool, Vcb->SparingBlockSize << Vcb->BlockSizeBits, 'bNWD');
        if(!tmp_buff) {
            UDFPrint(("  can't alloc tmp\n"));
            UDFReleaseResource(&(Vcb->IoResource));
            return STATUS_DEVICE_DATA_ERROR;
        }
        free_tmp = TRUE;
    }

    for(i=0; i<len; i++) {
        if(!Vcb->SparingCountFree) {
            UDFPrint(("  no more free spare blocks, abort verification\n"));
            break;
        }
        UDFPrint(("  read LBA %x (%x)\n", lba0+i, j));
        if(!j) {
            need_remap = FALSE;
            lba1 = lba0+i;
            non_zero = FALSE;
            if(single_packet) {
                // single packet requested
                tmp_buff = (PUCHAR)Buffer;
                if(Flags & PH_EX_WRITE) {
                    UDFPrint(("  remap single write\n"));
                    UDFPrint(("  try del from verify cache @ %x, %x\n", lba0, len));
                    UDFVForget(Vcb, len, UDFRelocateSector(Vcb, lba0), 0);
                    goto do_remap;
                } else {
                    UDFPrint(("  recover and remap single read\n"));
                }
            }
        }
        p = tmp_buff+(j<<Vcb->BlockSizeBits);
        // not cached, try to read
        // prepare for error, if block cannot be read, assume it is zero-filled
        RtlZeroMemory(p, Vcb->BlockSize);

        // check if block valid
        if(Vcb->BSBM_Bitmap) {
            if(UDFGetBit((uint32*)(Vcb->BSBM_Bitmap), UDFRelocateSector(Vcb, lba0+i))) {
                UDFPrint(("  remap: known BB @ %x, mapped to %x\n", lba0+i, UDFRelocateSector(Vcb, lba0+i)));
                need_remap = TRUE;
            }
        }
        zero = FALSE;
        if(Vcb->FSBM_Bitmap) {
            if(UDFGetFreeBit((uint32*)(Vcb->FSBM_Bitmap), lba0+i)) {
                UDFPrint(("  unused @ %x\n", lba0+i));
                zero = TRUE;
            }
        }
        if(!zero && Vcb->ZSBM_Bitmap) {
            if(UDFGetZeroBit((uint32*)(Vcb->ZSBM_Bitmap), lba0+i)) {
                UDFPrint(("  unused @ %x (Z)\n", lba0+i));
                zero = TRUE;
            }
        }
        non_zero |= !zero;

        if(!j) {
            packet_ok = FALSE;
            if(!single_packet) {
                // try to read entire packet, this returs error more often then sequential reading of all blocks one by one
                tmp_wb = (SIZE_T)_Vcb;
                RC = UDFTRead(_Vcb, p, Vcb->SparingBlockSize << Vcb->BlockSizeBits, lba0+i, &tmp_wb,
                              Flags | PH_READ_VERIFY_CACHE | PH_TMP_BUFFER | PH_VCB_IN_RETLEN);
            } else {
                // Note: we get here ONLY if original request failed
                // do not retry if it was single-packet request
                RC = STATUS_UNSUCCESSFUL;
            }
            if(RC == STATUS_SUCCESS) {
                UDFPrint(("  packet ok @ %x\n", lba0+i));
                packet_ok = TRUE;
                i += Vcb->SparingBlockSize-1;
                continue;
            } else {
                need_remap = TRUE;
            }
        }

        if(!zero) {
            if(WCacheIsCached__(&(Vcb->FastCache), lba0+i, 1)) {
                // even if block is cached, we have to verify if it is readable
                if(!packet_ok && !UDFVIsStored(Vcb, lba0+i)) {

                    tmp_wb = (SIZE_T)_Vcb;
                    RC = UDFTRead(_Vcb, p, Vcb->BlockSize, lba0+i, &tmp_wb,
                                  Flags | PH_FORGET_VERIFIED | PH_READ_VERIFY_CACHE | PH_TMP_BUFFER | PH_VCB_IN_RETLEN);
                    if(!OS_SUCCESS(RC)) {
                        UDFPrint(("  Found BB @ %x\n", lba0+i));
                    }

                }
                RC = WCacheDirect__(&(Vcb->FastCache), _Vcb, lba0+i, FALSE, &cached_block, TRUE/* cached only */);
            } else {
                cached_block = NULL;
                if(!packet_ok) {
                    RC = STATUS_UNSUCCESSFUL;
                } else {
                    RC = STATUS_SUCCESS;
                }
            }
            if(OS_SUCCESS(RC)) {
                // cached or successfully read
                if(cached_block) {
                    // we can get from cache the most fresh data
                    RtlCopyMemory(p, cached_block, Vcb->BlockSize);
                }

            } else {
                if(!UDFVIsStored(Vcb, lba0+i)) {
                    tmp_wb = (SIZE_T)_Vcb;
                    RC = UDFTRead(_Vcb, p, Vcb->BlockSize, lba0+i, &tmp_wb,
                                  Flags | PH_FORGET_VERIFIED | PH_READ_VERIFY_CACHE | PH_TMP_BUFFER | PH_VCB_IN_RETLEN);
                } else {
                    // get it from verify-cache
                    RC = STATUS_UNSUCCESSFUL;
                }
                if(!OS_SUCCESS(RC)) {
/*
                    UDFPrint(("  retry @ %x\n", lba0+i));
                    tmp_wb = (uint32)_Vcb;
                    RC = UDFTRead(_Vcb, p, Vcb->BlockSize, lba0+i, &tmp_wb,
                                  Flags | PH_FORGET_VERIFIED | PH_READ_VERIFY_CACHE | PH_TMP_BUFFER | PH_VCB_IN_RETLEN);
*/
                    UDFPrint(("  try get from verify cache @ %x\n", lba0+i));
                    RC = UDFVRead(Vcb, p, 1, UDFRelocateSector(Vcb, lba0+i),
                                  Flags | PH_FORGET_VERIFIED | PH_READ_VERIFY_CACHE | PH_TMP_BUFFER);
                    need_remap = TRUE;
                }
            }
        } else {
            RtlZeroMemory(p, Vcb->BlockSize);
        }
        if(!packet_ok) {
            UDFPrint(("  try del from verify cache @ %x\n", lba0+i));
            RC = UDFVForget(Vcb, 1, UDFRelocateSector(Vcb, lba0+i), 0);
        }

        if(!packet_ok || need_remap) {
            UDFPrint(("  block in bad packet @ %x\n", lba0+i));
            if(Vcb->BSBM_Bitmap) {
                UDFSetBit(Vcb->BSBM_Bitmap, lba0+i);
            }
            if(Vcb->FSBM_Bitmap) {
                UDFSetUsedBit(Vcb->FSBM_Bitmap, lba0+i);
            }
        }

        j++;
        if(j >= Vcb->SparingBlockSize) {
            // remap this packet
            if(need_remap) {
                ASSERT(!packet_ok);
                if(!non_zero) {
                    UDFPrint(("  forget Z packet @ %x\n", lba1));
                    UDFUnmapRange(Vcb, lba1, Vcb->SparingBlockSize);
                    RC = STATUS_SUCCESS;
                } else {
do_remap:
                    for(j=0; j<3; j++) {
                        UDFPrint(("  remap packet @ %x\n", lba1));
                        RC = UDFRemapPacket(Vcb, lba1, FALSE);
                        if(!OS_SUCCESS(RC)) {
                            if(RC == STATUS_SHARING_VIOLATION) {
                                UDFPrint(("  remap2\n"));
                                // remapped location have died
                                RC = UDFRemapPacket(Vcb, lba1, TRUE);
                            }
                            if(!OS_SUCCESS(RC)) {
                                // packet cannot be remapped :(
                                RC = STATUS_DEVICE_DATA_ERROR;
                            }
                        }
                        UDFPrint(("  remap status %x\n", RC));
                        if(OS_SUCCESS(RC)) {
                            // write to remapped area
                            tmp_wb = (SIZE_T)_Vcb;
                            RC = UDFTWrite(_Vcb, tmp_buff, Vcb->SparingBlockSize << Vcb->BlockSizeBits, lba1, &tmp_wb,
                                          Flags | PH_FORGET_VERIFIED | PH_READ_VERIFY_CACHE | PH_TMP_BUFFER | PH_VCB_IN_RETLEN);
                            UDFPrint(("  write status %x\n", RC));
                            if(RC != STATUS_SUCCESS) {
                                // will be remapped
                                UDFPrint(("  retry remap\n"));

                                // Note: when remap of already remapped block is requested, verify of
                                // entire sparing are will be performed.

                            } else {
                                UDFPrint(("  remap OK\n"));
                                break;
                            }
                        } else {
                            UDFPrint(("  failed remap\n"));
                            break;
                        }
                    } // for
                }
                if(!OS_SUCCESS(RC) && !OS_SUCCESS(final_RC)) {
                    final_RC = RC;
                }
            } else {
                UDFPrint(("  NO remap for @ %x\n", (lba0+i) & ~mask));
            }
            j=0;
        }
    }
    if(free_tmp) {
        DbgFreePool(tmp_buff);
    }

    tmp_wb = (SIZE_T)_Vcb;
    if(Flags & PH_EX_WRITE) {
        UDFPrint(("IO-Write-Verify (2)\n"));
        //RC = UDFTWrite(_Vcb, Buffer, Length, LBA, &tmp_wb, Flags | PH_FORGET_VERIFIED | PH_VCB_IN_RETLEN);
    } else {
        UDFPrint(("IO-Read-Verify (2)\n"));
        RC = UDFTRead(_Vcb, Buffer, Length, LBA, &tmp_wb, Flags | PH_FORGET_VERIFIED | PH_VCB_IN_RETLEN);
    }
    (*IOBytes) = tmp_wb;
    UDFPrint(("Final %x\n", RC));

    UDFReleaseResource(&(Vcb->IoResource));
    if(Flags & PH_LOCK_CACHE) {
        WCacheEODirect__(&(Vcb->FastCache), Vcb);
    }

    return RC;
} // end UDFTIOVerify()

OSSTATUS
UDFTWriteVerify(
    IN void* _Vcb,
    IN void* Buffer,     // Target buffer
    IN SIZE_T Length,
    IN uint32 LBA,
    OUT PSIZE_T WrittenBytes,
    IN uint32 Flags
    )
{
    return UDFTIOVerify(_Vcb, Buffer, Length, LBA, WrittenBytes, Flags | PH_VCB_IN_RETLEN | PH_EX_WRITE | PH_KEEP_VERIFY_CACHE);
} // end UDFTWriteVerify()

OSSTATUS
UDFTReadVerify(
    IN void* _Vcb,
    IN void* Buffer,     // Target buffer
    IN SIZE_T Length,
    IN uint32 LBA,
    OUT PSIZE_T ReadBytes,
    IN uint32 Flags
    )
{
    return UDFTIOVerify(_Vcb, Buffer, Length, LBA, ReadBytes, Flags | PH_VCB_IN_RETLEN | PH_KEEP_VERIFY_CACHE);
} // end UDFTReadVerify()
#endif //_BROWSE_UDF_

/*
    This routine performs low-level write

    ATTENTION! When we are in Variable-Packet mode (CDR_Mode = TRUE)
    LBA is ignored and assumed to be equal to NWA by CD-R(W) driver
 */
OSSTATUS
UDFTWrite(
    IN void* _Vcb,
    IN void* Buffer,     // Target buffer
    IN SIZE_T Length,
    IN uint32 LBA,
    OUT PSIZE_T WrittenBytes,
    IN uint32 Flags
    )
{
#ifndef UDF_READ_ONLY_BUILD
#define Vcb ((PVCB)_Vcb)

#ifdef _BROWSE_UDF_
    PEXTENT_MAP RelocExtent;
    PEXTENT_MAP RelocExtent_saved = NULL;
#endif //_BROWSE_UDF_
    uint32 retry;
    BOOLEAN res_acq = FALSE;

    OSSTATUS RC = STATUS_SUCCESS;
    uint32 rLba;
    uint32 BCount;
    uint32 i;

#ifdef DBG
    //ASSERT(!(LBA & (32-1)));
#endif //DBG

    (*WrittenBytes) = 0;
    BCount = Length>>Vcb->BlockSizeBits;

    UDFPrint(("TWrite %x (%x)\n", LBA, BCount));
#ifdef _BROWSE_UDF_
    if(Vcb->VCBFlags & UDF_VCB_FLAGS_DEAD) {
        UDFPrint(("DEAD\n"));
        return STATUS_NO_SUCH_DEVICE;
    }

    Vcb->VCBFlags |= (UDF_VCB_SKIP_EJECT_CHECK | UDF_VCB_LAST_WRITE);
    if(!Vcb->CDR_Mode) {
        RelocExtent = UDFRelocateSectors(Vcb, LBA, BCount);
        if(!RelocExtent) {
            UDFPrint(("can't relocate\n"));
            return STATUS_INSUFFICIENT_RESOURCES;
        }
        rLba = LBA;
    } else {
        RelocExtent = UDF_NO_EXTENT_MAP;
        rLba = Vcb->NWA;
    }
#else //_BROWSE_UDF_
    rLba = LBA;
#endif //_BROWSE_UDF_

#ifdef DBG
    //ASSERT(!(rLba & (32-1)));
#endif //DBG

    _SEH2_TRY {
#ifdef _BROWSE_UDF_

        if(!(Flags & PH_IO_LOCKED)) {
            UDFAcquireResourceExclusive(&(Vcb->IoResource), TRUE);
            res_acq = TRUE;
        }

        if(RelocExtent == UDF_NO_EXTENT_MAP) {
#endif //_BROWSE_UDF_
            retry = UDF_WRITE_MAX_RETRY;
retry_1:
            RC = UDFPrepareForWriteOperation(Vcb, rLba, BCount);
            if(!OS_SUCCESS(RC)) {
                UDFPrint(("prepare failed\n"));
                try_return(RC);
            }
            if(Flags & PH_VCB_IN_RETLEN) {
                (*WrittenBytes) = (ULONG_PTR)Vcb;
            }
            RC = UDFPhWriteVerifySynchronous(Vcb->TargetDeviceObject, Buffer, Length,
                       ((uint64)rLba) << Vcb->BlockSizeBits, WrittenBytes, Flags);
#ifdef _BROWSE_UDF_
            Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
#endif //_BROWSE_UDF_
            if(!OS_SUCCESS(RC) &&
                OS_SUCCESS(RC = UDFRecoverFromError(Vcb, TRUE, RC, rLba, BCount, &retry)) )
                goto retry_1;
            UDFUpdateNWA((PVCB)_Vcb, rLba, BCount, RC);
            try_return(RC);
#ifdef _BROWSE_UDF_
        }
        // write according to relocation table
        RelocExtent_saved = RelocExtent;
        for(i=0; RelocExtent->extLength; i++, RelocExtent++) {
            SIZE_T _WrittenBytes;
            rLba = RelocExtent->extLocation;
            BCount = RelocExtent->extLength>>Vcb->BlockSizeBits;
            retry = UDF_WRITE_MAX_RETRY;
retry_2:
            RC = UDFPrepareForWriteOperation(Vcb, rLba, BCount);
            if(!OS_SUCCESS(RC)) {
                UDFPrint(("prepare failed (2)\n"));
                break;
            }
            if(Flags & PH_VCB_IN_RETLEN) {
                _WrittenBytes = (ULONG_PTR)Vcb;
            }
            RC = UDFPhWriteVerifySynchronous(Vcb->TargetDeviceObject, Buffer, RelocExtent->extLength,
                       ((uint64)rLba) << Vcb->BlockSizeBits, &_WrittenBytes, Flags);
            Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
            if(!OS_SUCCESS(RC) &&
                OS_SUCCESS(RC = UDFRecoverFromError(Vcb, TRUE, RC, rLba, BCount, &retry)) )
                goto retry_2;
            UDFUpdateNWA((PVCB)_Vcb, rLba, BCount, RC);
            LBA += BCount;
            (*WrittenBytes) += _WrittenBytes;
            if(!OS_SUCCESS(RC)) break;
            *((uint32*)&Buffer) += RelocExtent->extLength;
        }
#endif //_BROWSE_UDF_
try_exit: NOTHING;
    } _SEH2_FINALLY {
        if(res_acq) {
            UDFReleaseResource(&(Vcb->IoResource));
        }
#ifdef _BROWSE_UDF_
        if(RelocExtent_saved) {
            MyFreePool__(RelocExtent_saved);
        }
#endif //_BROWSE_UDF_
    } _SEH2_END;
    UDFPrint(("TWrite: %x\n", RC));
    return RC;

#undef Vcb
#else //UDF_READ_ONLY_BUILD
    return STATUS_ACCESS_DENIED;
#endif //UDF_READ_ONLY_BUILD
} // end UDFTWrite()

/*
    This routine performs low-level read
 */
OSSTATUS
UDFTRead(
    IN void* _Vcb,
    IN void* Buffer,     // Target buffer
    IN SIZE_T Length,
    IN uint32 LBA,
    OUT PSIZE_T ReadBytes,
    IN uint32 Flags
    ) 
{
    uint32 rLba;
    OSSTATUS RC = STATUS_SUCCESS;
    uint32 retry;
    PVCB Vcb = (PVCB)_Vcb;
    uint32 BCount = Length >> Vcb->BlockSizeBits;
    uint32 i;
#ifdef _BROWSE_UDF_
    PEXTENT_MAP RelocExtent;
    PEXTENT_MAP RelocExtent_saved = NULL;
    BOOLEAN res_acq = FALSE;
//    LARGE_INTEGER delay;
    Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;

    ASSERT(Buffer);

    (*ReadBytes) = 0;

    if(Vcb->VCBFlags & UDF_VCB_FLAGS_DEAD)
        return STATUS_NO_SUCH_DEVICE;

    RelocExtent = UDFRelocateSectors(Vcb, LBA, BCount);
    if(!RelocExtent) return STATUS_INSUFFICIENT_RESOURCES;

    _SEH2_TRY {

        if(!(Flags & PH_IO_LOCKED)) {
            UDFAcquireResourceExclusive(&(Vcb->IoResource), TRUE);
            res_acq = TRUE;
        }

        if(RelocExtent == UDF_NO_EXTENT_MAP) {
            rLba = LBA;
            if(rLba >= (Vcb->CDR_Mode ? Vcb->NWA : Vcb->LastLBA + 1)) {
                RtlZeroMemory(Buffer, Length);
                try_return(RC = STATUS_SUCCESS);
            }
            retry = UDF_WRITE_MAX_RETRY;
retry_1:
            RC = UDFPrepareForReadOperation(Vcb, rLba, Length >> Vcb->BlockSizeBits);
            if(!OS_SUCCESS(RC)) try_return(RC);
            rLba = UDFFixFPAddress(Vcb, rLba);
#else
            rLba = LBA;
            retry = UDF_WRITE_MAX_RETRY;
retry_1:
            RC = UDFPrepareForReadOperation(Vcb, rLba, Length >> Vcb->BlockSizeBits);
            if(!OS_SUCCESS(RC)) return RC; // this is for !_BROWSE_UDF only
#endif //_BROWSE_UDF_
            if(Flags & PH_VCB_IN_RETLEN) {
                (*ReadBytes) = (SIZE_T)Vcb;
            }
            RC = UDFPhReadSynchronous(Vcb->TargetDeviceObject, Buffer, Length,
                       ((uint64)rLba) << Vcb->BlockSizeBits, ReadBytes, Flags);
            Vcb->VCBFlags &= ~UDF_VCB_LAST_WRITE;
#ifdef _BROWSE_UDF_
            Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
#endif //_BROWSE_UDF_
            if(!OS_SUCCESS(RC) &&
                OS_SUCCESS(RC = UDFRecoverFromError(Vcb, FALSE, RC, rLba, BCount, &retry)) ) {
                if(RC != STATUS_BUFFER_ALL_ZEROS) {
                    goto retry_1;
                }
                RtlZeroMemory(Buffer, Length);
                (*ReadBytes) = Length;
                RC = STATUS_SUCCESS;
            }
#ifdef _BROWSE_UDF_
            try_return(RC);
        }
        // read according to relocation table
        RelocExtent_saved = RelocExtent;
        for(i=0; RelocExtent->extLength; i++, RelocExtent++) {
            SIZE_T _ReadBytes;
            rLba = RelocExtent->extLocation;
            if(rLba >= (Vcb->CDR_Mode ? Vcb->NWA : Vcb->LastLBA + 1)) {
                RtlZeroMemory(Buffer, _ReadBytes = RelocExtent->extLength);
                RC = STATUS_SUCCESS;
                goto TR_continue;
            }
            BCount = RelocExtent->extLength>>Vcb->BlockSizeBits;
            retry = UDF_WRITE_MAX_RETRY;
retry_2:
            RC = UDFPrepareForReadOperation(Vcb, rLba, RelocExtent->extLength >> Vcb->BlockSizeBits);
            if(!OS_SUCCESS(RC)) break;
            rLba = UDFFixFPAddress(Vcb, rLba);
            if(Flags & PH_VCB_IN_RETLEN) {
                _ReadBytes = (SIZE_T)Vcb;
            }
            RC = UDFPhReadSynchronous(Vcb->TargetDeviceObject, Buffer, RelocExtent->extLength,
                       ((uint64)rLba) << Vcb->BlockSizeBits, &_ReadBytes, Flags);
            Vcb->VCBFlags &= ~UDF_VCB_LAST_WRITE;
            Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
            if(!OS_SUCCESS(RC) &&
                OS_SUCCESS(RC = UDFRecoverFromError(Vcb, FALSE, RC, rLba, BCount, &retry)) ) {
                if(RC != STATUS_BUFFER_ALL_ZEROS) {
                    goto retry_2;
                }
                RtlZeroMemory(Buffer, RelocExtent->extLength);
                _ReadBytes = RelocExtent->extLength;
                RC = STATUS_SUCCESS;
            }
TR_continue:
            (*ReadBytes) += _ReadBytes;
            if(!OS_SUCCESS(RC)) break;
            *((uint32*)&Buffer) += RelocExtent->extLength;
        }
try_exit: NOTHING;
    } _SEH2_FINALLY {
        if(res_acq) {
            UDFReleaseResource(&(Vcb->IoResource));
        }
        if(RelocExtent_saved) {
            MyFreePool__(RelocExtent_saved);
        }
    } _SEH2_END;
#endif //_BROWSE_UDF_
    return RC;
} // end UDFTRead()

#ifdef UDF_ASYNC_IO
/*
    This routine performs asynchronous low-level read
    Is not used now.
 */
OSSTATUS
UDFTReadAsync(
    IN void* _Vcb,
    IN void* _WContext,
    IN void* Buffer,     // Target buffer
    IN SIZE_T Length,
    IN uint32 LBA,
    OUT PSIZE_T ReadBytes
    ) 
{
    PEXTENT_MAP RelocExtent;
    PEXTENT_MAP RelocExtent_saved;
    OSSTATUS RC = STATUS_SUCCESS;
//    LARGE_INTEGER delay;
    uint32 retry = UDF_READ_MAX_RETRY;
    PVCB Vcb = (PVCB)_Vcb;
    Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
    uint32 rLba;
    uint32 BCount;

    ASSERT(Buffer);

    (*ReadBytes) = 0;

    RelocExtent = UDFRelocateSectors(Vcb, LBA, BCount = Length >> Vcb->BlockSizeBits);
    if(!RelocExtent) return STATUS_INSUFFICIENT_RESOURCES;
    if(RelocExtent == UDF_NO_EXTENT_MAP) {
        rLba = LBA;
        if(rLba >= (Vcb->CDR_Mode ? Vcb->NWA : Vcb->LastLBA + 1)) {
            RtlZeroMemory(Buffer, Length);
            return STATUS_SUCCESS;
        }
retry_1:
        RC = UDFPrepareForReadOperation(Vcb, rLba, BCount);
        if(!OS_SUCCESS(RC)) return RC;
        rLba = UDFFixFPAddress(Vcb, rLba);
        RC = UDFPhReadSynchronous(Vcb->TargetDeviceObject, Buffer, Length,
                   ((uint64)rLba) << Vcb->BlockSizeBits, ReadBytes, 0);
        Vcb->VCBFlags &= ~UDF_VCB_LAST_WRITE;
        Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
        if(!OS_SUCCESS(RC) &&
            OS_SUCCESS(RC = UDFRecoverFromError(Vcb, FALSE, RC, rLba, BCount, &retry)) )
            goto retry_1;
        return RC;
    }
    // read according to relocation table
    RelocExtent_saved = RelocExtent;
    for(uint32 i=0; RelocExtent->extLength; i++, RelocExtent++) {
        SIZE_T _ReadBytes;
        rLba = RelocExtent->extLocation;
        if(rLba >= (Vcb->CDR_Mode ? Vcb->NWA : Vcb->LastLBA + 1)) {
            RtlZeroMemory(Buffer, _ReadBytes = RelocExtent->extLength);
            RC = STATUS_SUCCESS;
            goto TR_continue;
        }
        BCount = RelocExtent->extLength>>Vcb->BlockSizeBits;
retry_2:
        RC = UDFPrepareForReadOperation(Vcb, rLba, RelocExtent->extLength >> Vcb->BlockSizeBits);
        if(!OS_SUCCESS(RC)) break;
        rLba = UDFFixFPAddress(Vcb, rLba);
        RC = UDFPhReadSynchronous(Vcb->TargetDeviceObject, Buffer, RelocExtent->extLength,
                   ((uint64)rLba) << Vcb->BlockSizeBits, &_ReadBytes, 0);
        Vcb->VCBFlags &= ~UDF_VCB_LAST_WRITE;
        Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
        if(!OS_SUCCESS(RC) &&
            OS_SUCCESS(RC = UDFRecoverFromError(Vcb, FALSE, RC, rLba, BCount, &retry)) )
            goto retry_2;
TR_continue:
        (*ReadBytes) += _ReadBytes;
        if(!OS_SUCCESS(RC)) break;
        *((uint32*)&Buffer) += RelocExtent->extLength;
    }
    MyFreePool__(RelocExtent_saved);
    return RC;
} // end UDFTReadAsync()

#endif //UDF_ASYNC_IO

/*

 */
NTSTATUS
UDFSetMRWMode(
    IN PVCB Vcb
    )
{
    GET_MRW_MODE_USER_OUT MRWPage;
    OSSTATUS RC;

    if(Vcb->MediaClassEx != CdMediaClass_CDRW)
        return STATUS_SUCCESS;
//#ifdef _BROWSE_UDF_
    if(Vcb->CompatFlags & UDF_VCB_IC_MRW_ADDR_PROBLEM)
        return STATUS_SUCCESS;
//#endif //_BROWSE_UDF_

    if(!Vcb->MRWStatus) {
        UDFPrint(("Non-MRW disk. Skip setting MRW_MODE\n"));
        return STATUS_SUCCESS;
    }
    UDFPrint(("try set MRW_MODE\n"));
    RC = UDFPhSendIOCTL(IOCTL_CDRW_GET_MRW_MODE, Vcb->TargetDeviceObject,
                    NULL,0,
                    (PVOID)&MRWPage,sizeof(MRWPage),
                    FALSE, NULL);
    if(!NT_SUCCESS(RC)) {
        return RC;
    }
    UDFPrint(("GET_MRW_MODE ok (current %x)\n", MRWPage.AddressMode));
    MRWPage.AddressMode = Vcb->MRWStatus ? 0 : MrwPage_use_GAA;
    UDFPrint(("SET_MRW_MODE %x\n", MRWPage.AddressMode));
    RC = UDFPhSendIOCTL(IOCTL_CDRW_SET_MRW_MODE, Vcb->TargetDeviceObject,
                    (PVOID)&MRWPage,sizeof(MRWPage),
                    NULL,0,
                    FALSE, NULL);
    UDFPrint(("SET_MRW_MODE status %x\n", RC));

    return STATUS_SUCCESS;
} // end UDFSetMRWMode()

OSSTATUS
UDFDoOPC(
    IN PVCB Vcb
    )
{
    OSSTATUS RC;
    if(Vcb->OPCNum && !Vcb->OPCDone) {
        UDFPrint(("UDFDoOPC\n"));
        if(!Vcb->OPCh) {
            Vcb->OPCh =
                (PSEND_OPC_INFO_HEADER_USER_IN)MyAllocatePool__(NonPagedPool,
                    sizeof(SEND_OPC_INFO_HEADER_USER_IN) );
        }
        if(!Vcb->OPCh)
            return STATUS_INSUFFICIENT_RESOURCES;
        Vcb->OPCh->DoOpc = TRUE;
        Vcb->OPCh->OpcBlocksNumber = 0;
        RC = UDFPhSendIOCTL(IOCTL_CDRW_SEND_OPC_INFO, Vcb->TargetDeviceObject,
                        (void*)(Vcb->OPCh),sizeof(SEND_OPC_INFO_HEADER_USER_IN),
                        NULL,0,
                        FALSE, NULL);
        if(!OS_SUCCESS(RC)) {
            UDFPrint(("UDFDoOPC failed\n"));
            Vcb->OPCNum = 0;
//            Vcb->VCBFlags |= UDF_VCB_FLAGS_OPC_FAILED;
        }
        Vcb->OPCDone = TRUE;
    }
    return RC;
} // end UDFDoOPC()

/*
    This routine performs media-type dependent preparations
    for write operation.

    For CDR/RW it sets WriteParameters according to track parameters,
    in some cases issues SYNC_CACHE command.
    It can also send OPC info if requered.
    If write-requested block is located beyond last formatted LBA
    on incompletely formatted DVD media, this routine performs
    all neccessary formatting operations in order to satisfy
    subsequent write request.
 */
OSSTATUS
UDFPrepareForWriteOperation(
    IN PVCB Vcb,
    IN uint32 Lba,
    IN uint32 BCount
    )
{
#ifndef UDF_READ_ONLY_BUILD
#ifdef UDF_FORMAT_MEDIA
    PUDFFmtState            fms = Vcb->fms;
#else 
  #define fms FALSE
#endif //UDF_FORMAT_MEDIA

#ifdef _UDF_STRUCTURES_H_
    if(Vcb->BSBM_Bitmap) {
        ULONG i;
        for(i=0; i<BCount; i++) {
            if(UDFGetBit((uint32*)(Vcb->BSBM_Bitmap), Lba+i)) {
                UDFPrint(("W: Known BB @ %#x\n", Lba));
                //return STATUS_FT_WRITE_RECOVERY; // this shall not be treated as error and
                                                   // we shall get IO request to BAD block
                return STATUS_DEVICE_DATA_ERROR;
            }
        }
    }
#endif //_UDF_STRUCTURES_H_

    Vcb->VCBFlags |= UDF_VCB_LAST_WRITE;

    if( 
#ifdef _BROWSE_UDF_
       (((Vcb->FsDeviceType != FILE_DEVICE_CD_ROM_FILE_SYSTEM) ||
       !(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) ||
        (Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK))
        && !fms
        ) ||
#endif //_BROWSE_UDF_
#ifdef UDF_FORMAT_MEDIA
        (fms && fms->SkipPrepareW) ||
#endif //UDF_FORMAT_MEDIA
       !(Vcb->VCBFlags & UDF_VCB_FLAGS_OUR_DEVICE_DRIVER)
       ) {
        UDFPrint(("Skip prepare for Write @%x\n", Lba));
        return STATUS_SUCCESS;
    }

    // check if the device requires OPC before each write operation
    UDFDoOPC(Vcb);

    if(Vcb->SyncCacheState == SYNC_CACHE_RECOVERY_ATTEMPT) {
        Vcb->SyncCacheState = SYNC_CACHE_RECOVERY_RETRY;
    } else {
        Vcb->SyncCacheState = SYNC_CACHE_RECOVERY_NONE;
    }
    if(Vcb->LastModifiedTrack &&
       (Vcb->TrackMap[Vcb->LastModifiedTrack].FirstLba <= Lba) &&
       (Vcb->TrackMap[Vcb->LastModifiedTrack].LastLba >= Lba) &&
       !( (Vcb->MediaClassEx == CdMediaClass_DVDRW ||
           Vcb->MediaClassEx == CdMediaClass_DVDpRW ||
           Vcb->MediaClassEx == CdMediaClass_DVDRAM ||
           Vcb->MRWStatus == DiscInfo_BGF_Interrupted ||
           Vcb->MRWStatus == DiscInfo_BGF_InProgress) && (Lba > Vcb->LastLBA))
      ) {
        // Ok, we needn't change Write Parameters
//        if(Vcb->TrackMap[Vcb->LastModifiedTrack].Flags & TrackMap_Try_variation)
//            Vcb->TrackMap[Vcb->LastModifiedTrack].Flags |= TrackMap_Use_variation;
        UDFPrint(("Skip prepare for Write (2) @%x\n", Lba));
        return STATUS_SUCCESS;
    }

    UDFSetMRWMode(Vcb);

    if(!UDFIsWriteParamsReq(Vcb)) {
#ifdef UDF_FORMAT_MEDIA
        if(fms) {
            return STATUS_SUCCESS;
        }
#endif //UDF_FORMAT_MEDIA
    }

    for(uint32 i=Vcb->FirstTrackNum; i<=Vcb->LastTrackNum; i++) {
        if((Vcb->TrackMap[i].FirstLba > Lba) ||
           (Vcb->TrackMap[i].LastLba < Lba)) {
            //UDFPrint(("not in track %d\n"));
            continue;
        }
        OSSTATUS RC;
        PGET_WRITE_MODE_USER_OUT WParams;

        if(!UDFIsWriteParamsReq(Vcb)) {
            RC = STATUS_SUCCESS;
            goto check_dvd_bg_format;
        }

        if(!Vcb->WParams) {
            Vcb->WParams =
                (PGET_WRITE_MODE_USER_OUT)MyAllocatePool__(NonPagedPool, 512);
        }
        if(!(WParams = Vcb->WParams)) {
            UDFPrint(("!WParams\n"));
            return STATUS_INSUFFICIENT_RESOURCES;
        }

        RC = UDFPhSendIOCTL(IOCTL_CDRW_GET_WRITE_MODE, Vcb->TargetDeviceObject,
                        NULL,0,
                        (void*)(Vcb->WParams),sizeof(GET_WRITE_MODE_USER_OUT),
                        FALSE, NULL);
        if(!OS_SUCCESS(RC)) {
#ifdef UDF_FORMAT_MEDIA
            if(fms) {
                fms->SkipPrepareW = 1;
                MyFreePool__(WParams);
                return STATUS_SUCCESS;
            }
#endif //UDF_FORMAT_MEDIA
            UDFPrint(("!get WParams\n"));
            return RC;
        }
        // clear unnecassary flags
        WParams->Byte2.Flags &= ~WParam_TestWrite;
        WParams->Byte2.Flags &= ~WParam_WType_Mask;
        // select packet writing
        WParams->Byte2.Flags |= WParam_WType_Packet;

        WParams->Byte3.Flags &= ~(WParam_TrkMode_Mask |
                                  WParam_TrkMode_AllowCpy |
                                  WParam_Copy);
        WParams->Byte3.Flags |= Vcb->TrackMap[i].TrackParam &
                                 (WParam_TrkMode_Mask |
                                  WParam_TrkMode_AllowCpy |
                                  WParam_Copy);

        // set packet type (VP/FP)
//        if(opt_partition == PT_VAT15 ||
//           opt_blank_vat15) 
        if(WParams->Byte2.Flags & WParam_LS_V) {
            WParams->LinkSize = 7;
        }

        if(Vcb->TrackMap[i].DataParam & TrkInfo_Packet) {
            if((Vcb->TrackMap[i].DataParam & TrkInfo_FP) &&
                !Vcb->CDR_Mode) {
                WParams->Byte3.Flags |= WParam_FP;
            } else {
                WParams->Byte3.Flags &= ~WParam_FP;
            }
        } else {
            if(!Vcb->CDR_Mode) {
                WParams->Byte3.Flags |= WParam_FP;
            } else {
                WParams->Byte3.Flags &= ~WParam_FP;
            }
        }

        // select multisession mode
        WParams->Byte3.Flags &= ~WParam_MultiSes_Mask;
        if((Vcb->DiscStat & DiscInfo_Disk_Mask) == DiscInfo_Disk_Appendable) {
            WParams->Byte3.Flags |= WParam_Multises_Multi;
        } else
        if(Vcb->LastSession > 1) {
            WParams->Byte3.Flags |= WParam_Multises_Final;
        } else {
            WParams->Byte3.Flags |= WParam_Multises_None;
        }
        // set sector mode (Mode1/XA)
        WParams->Byte4.Flags &= ~WParam_BlkType_Mask;
        if((Vcb->TrackMap[i].DataParam & TrkInfo_Dat_Mask) == TrkInfo_Dat_XA) {
            // XA Mode2
            WParams->Byte4.Flags |= WParam_BlkType_M2XAF1_2048;
            WParams->SesFmt = WParam_SesFmt_CdRomXa;
        } else if((Vcb->TrackMap[i].DataParam & TrkInfo_Dat_Mask) == TrkInfo_Dat_Mode1) {
            // Mode1
            WParams->Byte4.Flags |= WParam_BlkType_M1_2048;
            WParams->SesFmt = WParam_SesFmt_CdRom;
        } else {
#ifdef UDF_FORMAT_MEDIA
            if(fms) {
                fms->SkipPrepareW = 1;
                MyFreePool__(WParams);
                return STATUS_SUCCESS;
            }
#endif //UDF_FORMAT_MEDIA
            UDFPrint(("  inv sector mode\n"));
            return STATUS_INVALID_PARAMETER;
        }
        // set packet size
        *((uint32*)&(WParams->PacketSize)) = BCount;
        *((uint32*)&(WParams->SubHeader)) = 0;
        // set additional flags for VP

        if(Vcb->CDR_Mode) {
//        if(opt_partition == PT_VAT15) 
            WParams->SubHeader.Params.Params1.SubMode = WParam_SubHdr_SubMode1;
        }
        WParams->PageLength = sizeof(GET_WRITE_MODE_USER_OUT)-2;
        WParams->PageCode = MODE_PAGE_WRITE_PARAMS;
        // apply write parameters
        RC = UDFPhSendIOCTL(IOCTL_CDRW_SET_WRITE_MODE, Vcb->TargetDeviceObject,
                        (void*)WParams,sizeof(SET_WRITE_MODE_USER_IN),
                        NULL,0,FALSE,NULL);

#ifdef UDF_FORMAT_MEDIA
        if(fms) {
            if(!NT_SUCCESS(RC)) {
                fms->SkipPrepareW = 1;
                MyFreePool__(WParams);
                return STATUS_SUCCESS;
            }

            RC = UDFPhSendIOCTL(IOCTL_CDRW_GET_WRITE_MODE, Vcb->TargetDeviceObject,
                            NULL,0,
                            (PVOID)WParams,sizeof(GET_WRITE_MODE_USER_OUT),
                            FALSE, NULL);
            if(!NT_SUCCESS(RC)) {
                MyFreePool__(WParams);
                return RC;
            }

            if(fms->opt_partition == PT_VAT15 ||
               fms->opt_blank_vat15) {
                if(WParams->Byte3.Flags & WParam_FP) {
                    MyFreePool__(WParams);
                    return STATUS_INVALID_DEVICE_STATE;
                }
            } else {
                if(!(WParams->Byte3.Flags & WParam_FP)) {
                    MyFreePool__(WParams);
                    return STATUS_INVALID_DEVICE_STATE;
                }
            }
        }
#endif //UDF_FORMAT_MEDIA
        
        // switch to random access mode
        ((PSET_RANDOM_ACCESS_USER_IN)WParams)->RandomAccessMode = Vcb->CDR_Mode ? FALSE : TRUE;
//        ((PSET_RANDOM_ACCESS_USER_IN)WParams)->RandomAccessMode = (opt_partition != PT_VAT15) ? TRUE : FALSE;
        RC = UDFPhSendIOCTL(IOCTL_CDRW_SET_RANDOM_ACCESS, Vcb->TargetDeviceObject,
                        (void*)WParams,sizeof(SET_RANDOM_ACCESS_USER_IN),
                        NULL,0,FALSE, NULL);

check_dvd_bg_format:

        UDFPrint(("  check BGF\n"));
        if(!Vcb->CDR_Mode) {
            if(OS_SUCCESS(RC)) {
                Vcb->LastModifiedTrack = i;
                if(!(Vcb->TrackMap[i].Flags & TrackMap_Use_variation)) {
                    if(Vcb->TrackMap[i].Flags & TrackMap_Try_variation) {
                        Vcb->TrackMap[i].Flags |= TrackMap_Use_variation;
                    } else {
                        Vcb->TrackMap[i].Flags |= TrackMap_Try_variation;
                    }
                }
            }
        } else {
            Vcb->LastModifiedTrack = 0;
        }
//        fms->SkipPrepareW = 1;


        if((Vcb->MediaClassEx == CdMediaClass_DVDRW ||
            Vcb->MediaClassEx == CdMediaClass_DVDpRW ||
            Vcb->MediaClassEx == CdMediaClass_DVDRAM ||
            Vcb->MRWStatus == DiscInfo_BGF_Interrupted )
                 && (Lba > Vcb->LastLBA)) {
   
            ULONG fLba;
            SIZE_T WrittenBytes;
            ULONG PSz = BCount << Vcb->BlockSizeBits;
#ifdef _BROWSE_UDF_
            ULONG retry;
#endif //_BROWSE_UDF_
            PFORMAT_CDRW_PARAMETERS_USER_IN ForBuf;

            ASSERT((Vcb->LastLBA+1) == Vcb->NWA);

            if(Lba+BCount <= (Vcb->LastLBA+1) ) {
                UDFPrint(("DVD cont. fmt, LBA+BCount<=NWA, exiting\n"));
                return STATUS_SUCCESS;
            }
            if((Vcb->MRWStatus != DiscInfo_BGF_Interrupted) &&
               (Lba <= (Vcb->LastLBA+1)) ) {
                UDFPrint(("!PausedBGF + DVD cont. fmt, LBA<=NWA, exiting\n"));
                return STATUS_SUCCESS;
            }

            if(Vcb->MRWStatus == DiscInfo_BGF_Interrupted) {
                // This code also can restart background MRW formatting
                UDFPrint(("DVD cont. fmt, LastLBA %x, Lba %x\n", Vcb->LastLBA, Lba));

                ForBuf = (PFORMAT_CDRW_PARAMETERS_USER_IN)DbgAllocatePoolWithTag(NonPagedPool, sizeof(FORMAT_CDRW_PARAMETERS_USER_IN), 'zNWD');
                if(ForBuf) {
                    RtlZeroMemory(ForBuf, sizeof(FORMAT_CDRW_PARAMETERS_USER_IN));
                    ForBuf->Flags.FlagsEx = FORMAT_UNIT_RESTART_MRW;
                    ForBuf->BlockCount = 0xffffffff;

                    RC = UDFPhSendIOCTL(IOCTL_CDRW_FORMAT_UNIT, Vcb->TargetDeviceObject,
                            ForBuf,sizeof(FORMAT_CDRW_PARAMETERS_USER_IN),
                            NULL,0,FALSE, NULL);
                    DbgFreePool(ForBuf);
                    if(OS_SUCCESS(RC)) {
                        UDFPrint(("BGFormat restarted Interrupted->InProgress\n"));
                        Vcb->MRWStatus = DiscInfo_BGF_InProgress;
                    } else {
                        PGET_LAST_ERROR_USER_OUT Error = NULL;
                        if(!Vcb->Error) {
                            Vcb->Error = (PGET_LAST_ERROR_USER_OUT)
                                            MyAllocatePool__(NonPagedPool, sizeof(GET_LAST_ERROR_USER_OUT));
                        }
                        Error = Vcb->Error;
                        if(Error) {
                            UDFPhSendIOCTL( IOCTL_CDRW_GET_LAST_ERROR, Vcb->TargetDeviceObject,
                                            NULL,0,
                                            Error,sizeof(GET_LAST_ERROR_USER_OUT),
                                            TRUE,NULL);
                            UDFPrint(("SK=%x ASC=%x, ASCQ=%x, IE=%x\n",
                                     Error->SenseKey, Error->AdditionalSenseCode, Error->AdditionalSenseCodeQualifier, Error->LastError));
                            // check for Long Write In Progress
                            if( (Error->SenseKey == SCSI_SENSE_NOT_READY) &&
                                (Error->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
                                 ((Error->AdditionalSenseCodeQualifier == SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS) ||
                                  (Error->AdditionalSenseCodeQualifier == SCSI_SENSEQ_FORMAT_IN_PROGRESS)) ) {
                                RC = STATUS_SUCCESS;
                                UDFPrint(("Seems, BGFormat already restarted\n"));
                                Vcb->MRWStatus = DiscInfo_BGF_InProgress;
                            }
                        }
                    }
                }
            } else {
                RC = STATUS_SUCCESS;
            }

            UDFPrint(("DVD cont. write, LastLBA %x, Lba %x\n", Vcb->LastLBA, Lba));

            ASSERT(Vcb->MediaClassEx == CdMediaClass_DVDRW);
            if(!Vcb->fZBuffer) {
                Vcb->fZBuffer = (PCHAR)DbgAllocatePoolWithTag(NonPagedPool, PSz, 'zNWD');
                RtlZeroMemory(Vcb->fZBuffer, PSz);
                Vcb->fZBufferSize = PSz;
            } else
            if(Vcb->fZBufferSize < PSz) {
                PSz = Vcb->fZBufferSize;
            }
            if(!Vcb->fZBuffer) {
                BrutePoint();
                RC = STATUS_INSUFFICIENT_RESOURCES;
            } else {
                for(fLba = Vcb->NWA; fLba < Lba; fLba+=BCount) {
#ifdef _BROWSE_UDF_
                    retry = UDF_WRITE_MAX_RETRY;
retry_1:
#endif //_BROWSE_UDF_
                    RC = UDFPhWriteVerifySynchronous(Vcb->TargetDeviceObject, Vcb->fZBuffer, PSz,
                           ((uint64)fLba) << Vcb->BlockSizeBits, &WrittenBytes, PH_TMP_BUFFER);
                    Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
                    UDFPrint(("Fmt status: %x\n", RC));
#ifdef _BROWSE_UDF_
                    if(!OS_SUCCESS(RC) &&
                        OS_SUCCESS(RC = UDFRecoverFromError(Vcb, TRUE, RC, fLba, BCount, &retry)) ) {
                        goto retry_1;
                        UDFPrint(("Fmt retry\n"));
                    }
#endif //_BROWSE_UDF_
                    if(!OS_SUCCESS(RC)) {
                        BrutePoint();
                        UDFPrint(("Fmt break on ERROR\n"));
                        break;
                    }
                    UDFUpdateNWA(Vcb, fLba, BCount, RC);
                }
            }
        } else {
            UDFPrint(("  no special processing\n"));
        }
        
        return RC;
    }
#endif //UDF_READ_ONLY_BUILD
    UDFPrint(("  no suitable track!\n"));
    return STATUS_INVALID_PARAMETER;
} // end UDFPrepareForWriteOperation()

//#ifdef _BROWSE_UDF_
/*
    This routine tries to recover from hardware error
    Return: STATUS_SUCCESS - retry requst
            STATUS_XXX - unrecoverable error
 */
OSSTATUS
UDFRecoverFromError(
    IN PVCB Vcb,
    IN BOOLEAN WriteOp,
    IN OSSTATUS status,
    IN uint32 Lba,
    IN uint32 BCount,
 IN OUT uint32* retry
    )
{
    PGET_LAST_ERROR_USER_OUT Error = NULL;
    LARGE_INTEGER delay;
//    OSSTATUS RC;
    uint32 i;
    BOOLEAN UpdateBB = FALSE;

    if(!(*retry) ||
       !(Vcb->VCBFlags & UDF_VCB_FLAGS_OUR_DEVICE_DRIVER) ||
        (Vcb->FsDeviceType != FILE_DEVICE_CD_ROM_FILE_SYSTEM))
        return status;
    (*retry)--;
    // allocate tmp buffer
    _SEH2_TRY {
        if(!Vcb->Error) {
            if(!(Vcb->Error = (PGET_LAST_ERROR_USER_OUT)
                            MyAllocatePool__(NonPagedPool, sizeof(GET_LAST_ERROR_USER_OUT))))
                try_return(status);
        }
        if(status == STATUS_NO_SUCH_DEVICE) {
            UDFPrint(("Error recovery: STATUS_NO_SUCH_DEVICE, die.....\n"));
            Vcb->VCBFlags |= UDF_VCB_FLAGS_UNSAFE_IOCTL | UDF_VCB_FLAGS_DEAD;
            try_return(status);
        }

#ifdef _UDF_STRUCTURES_H_
        if(status == STATUS_NO_MEDIA_IN_DEVICE && !Vcb->EjectWaiter) {
            UDFPrint(("Error recovery: STATUS_NO_MEDIA_IN_DEVICE, prevent further remount.....\n"));
            // Make sure, that volume will never be quick-remounted
            // It is very important for ChkUdf utility and
            // some CD-recording libraries
            Vcb->SerialNumber--;
            try_return(status);
        }
#endif //_UDF_STRUCTURES_H_

        Error = Vcb->Error;
        UDFPhSendIOCTL( IOCTL_CDRW_GET_LAST_ERROR, Vcb->TargetDeviceObject,
                        NULL,0,
                        Error,sizeof(GET_LAST_ERROR_USER_OUT),
                        TRUE,NULL);
        UDFPrint(("SK=%x ASC=%x, ASCQ=%x, IE=%x\n",
                 Error->SenseKey, Error->AdditionalSenseCode, Error->AdditionalSenseCodeQualifier, Error->LastError));
        // check for Long Write In Progress
        if( ((Error->SenseKey == SCSI_SENSE_NOT_READY) &&
             (Error->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
             (Error->AdditionalSenseCodeQualifier == SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS)) ) {
            // we should wait...
            if(WriteOp) {
                if((*retry) == UDF_WRITE_MAX_RETRY-1) {
                    UDFPrint(("Error recovery: reserve retry count for write retries\n"));
                    (*retry) = UDF_WRITE_MAX_RETRY*3;
                } else
                if((*retry) == UDF_WRITE_MAX_RETRY) {
                    UDFPrint(("Error recovery: jump over UDF_WRITE_MAX_RETRY\n"));
                    (*retry)--;
                }
                delay.QuadPart = -500000; // 0.05 sec
                KeDelayExecutionThread(KernelMode, FALSE, &delay);
                if(WriteOp && ((*retry) > UDF_WRITE_MAX_RETRY-1)) {
                    UDFPrint(("Error recovery: simple write retry with delay\n"));
                    try_return(status = STATUS_SUCCESS);
                }
            } else {
                delay.QuadPart = -500000; // 0.05 sec
                KeDelayExecutionThread(KernelMode, FALSE, &delay);
                if((*retry) == UDF_WRITE_MAX_RETRY-1) {
                    UDFPrint(("Error recovery: retry read after small delay\n"));
                    try_return(status = STATUS_SUCCESS);
                }
            }
            UDFPrint(("Error recovery: sync cache\n"));
            // ...flush device cache...
            UDFSyncCache(Vcb);
            // wait again & retry
            delay.QuadPart = -1000000; // 0.1 sec
            KeDelayExecutionThread(KernelMode, FALSE, &delay);
#ifdef _UDF_STRUCTURES_H_
            if(Vcb->BGWriters) (*retry)++;
#endif //_UDF_STRUCTURES_H_
            try_return(status = STATUS_SUCCESS);
        } else
        // check for Long Write In Progress
        if((Error->SenseKey == SCSI_SENSE_NOT_READY) &&
           (Error->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
          ((Error->AdditionalSenseCodeQualifier == SCSI_SENSEQ_FORMAT_IN_PROGRESS) ||
           (Error->AdditionalSenseCodeQualifier == SCSI_SENSEQ_BECOMING_READY) ||
           (Error->AdditionalSenseCodeQualifier == SCSI_SENSEQ_OPERATION_IN_PROGRESS) ) ) {
            // we should wait & retry
            UDFPrint(("Error recovery: op. in progress, waiting 0.3 sec\n"));
            delay.QuadPart = -3000000; // 0.3 sec
            KeDelayExecutionThread(KernelMode, FALSE, &delay);
#ifdef _UDF_STRUCTURES_H_
            if(Vcb->BGWriters) (*retry)++;
#endif //_UDF_STRUCTURES_H_
            Vcb->SyncCacheState = SYNC_CACHE_RECOVERY_ATTEMPT;
            try_return(status = STATUS_SUCCESS);
        } else
        // check for non empty cache special case
        if((Error->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST) &&
           (Error->AdditionalSenseCode == SCSI_ADSENSE_INVALID_CMD_SEQUENCE)) {
            // we should wait & retry
            if(!WriteOp) {
                UDFPrint(("Error recovery: invalid command sequence on read\n"));
                delay.QuadPart = -1000000; // 0.1 sec
                KeDelayExecutionThread(KernelMode, FALSE, &delay);
                UDFPrint(("Error recovery: sync cache\n"));
                // ...flush device cache...
                UDFSyncCache(Vcb);
                // wait again & retry
                delay.QuadPart = -1000000; // 0.1 sec
                KeDelayExecutionThread(KernelMode, FALSE, &delay);
#ifdef _UDF_STRUCTURES_H_
                if(Vcb->BGWriters) (*retry)++;
#endif //_UDF_STRUCTURES_H_
                try_return(status = STATUS_SUCCESS);
            }
            goto reinit_sector_mode;
        } else
        // check for Bus Reset (sometimes it happends...)
        if((Error->SenseKey == SCSI_SENSE_UNIT_ATTENTION) &&
           (Error->AdditionalSenseCode == SCSI_ADSENSE_BUS_RESET) ) {
            // we should wait
            UDFPrint(("Error recovery: bus reset...\n"));
            Vcb->MediaChangeCount = Error->MediaChangeCount;
            delay.QuadPart = -1000000; // 0.1 sec
            KeDelayExecutionThread(KernelMode, FALSE, &delay);
            // reset driver
            UDFResetDeviceDriver(Vcb, Vcb->TargetDeviceObject, FALSE);
            delay.QuadPart = -1000000; // 0.1 sec
            KeDelayExecutionThread(KernelMode, FALSE, &delay);
            // lock it
/*            ((PPREVENT_MEDIA_REMOVAL_USER_IN)(Error))->PreventMediaRemoval = TRUE;
            UDFPhSendIOCTL( IOCTL_STORAGE_MEDIA_REMOVAL,
                                 Vcb->TargetDeviceObject,
                                 Error,sizeof(PREVENT_MEDIA_REMOVAL_USER_IN),
                                 NULL,0,
                                 FALSE,NULL);
            delay.QuadPart = -1000000; // 0.1 sec
            KeDelayExecutionThread(KernelMode, FALSE, &delay);*/

            // reinit write mode the following is performed inside UDFResetDeviceDriver()
            //Vcb->LastModifiedTrack = 0;
            //Vcb->OPCDone = FALSE;

reinit_sector_mode:
            // reinit sector mode
            Vcb->LastModifiedTrack = 0;
            UDFPrepareForWriteOperation(Vcb, Lba, BCount);
            try_return(status = STATUS_SUCCESS);
        } else
        // check for Illegal Sector Mode.
        // We can get this error 'cause of 2 reasons:
        // a) Bus reset occured. We should reinit
        // b) CopyProtection settings missmatch
        // c) preblems with DNA of firmware developer, some TEACs fall into such state
        //    after failed streaming read
        if((Error->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST) &&
           (Error->AdditionalSenseCode == SCSI_ADSENSE_ILLEGAL_MODE_FOR_THIS_TRACK)) {
bad_rw_seek_recovery:
            if(WriteOp) {

                if((*retry) <= 1) {
                    // Variate CopyProtection...
                    for(i=Vcb->FirstTrackNum; i<=Vcb->LastTrackNum; i++) {
                        if((Vcb->TrackMap[i].FirstLba > Lba) ||
                           (Vcb->TrackMap[i].LastLba < Lba))
                            continue;
    /*                    if(Vcb->TrackMap[i].Flags & TrackMap_CopyBit_variated)
                            // Last chance....
                            goto reinit_sector_mode;*/

                        // check if we have successuflly completed WriteOp
                        // using Variation.
                        // We should not variate these bits again in this case.
                        if(Vcb->TrackMap[i].Flags & TrackMap_Use_variation)
                            break;
                        Vcb->TrackMap[i].Flags &= ~TrackMap_Try_variation;
    /*                    if((Vcb->TrackMap[i].Flags & TrackMap_Try_variation) &&
                           (Vcb->TrackMap[i].Flags & (TrackMap_AllowCopyBit_variated |
                                                      TrackMap_CopyBit_variated)))
                            break;*/
    /*                    if(Vcb->TrackMap[i].Flags & TrackMap_Use_variation)
                            break;*/
                        Vcb->TrackMap[i].Flags |= TrackMap_Try_variation;
                        // Try variation. 
                        if(!(Vcb->TrackMap[i].Flags ^= TrackMap_AllowCopyBit_variated))
                            Vcb->TrackMap[i].Flags ^= TrackMap_CopyBit_variated;
                        if(Vcb->TrackMap[i].Flags & (TrackMap_AllowCopyBit_variated |
                                                     TrackMap_CopyBit_variated) ) {
                            (*retry) = 1;
                        } else {
                            Vcb->TrackMap[i].Flags &= ~TrackMap_Try_variation;
                        }
                        // reinit sector mode
                        Vcb->LastModifiedTrack = 0;
                        UDFPrepareForWriteOperation(Vcb, Lba, BCount);
                        break;
                    }
                } else {
                    // Reinit...
//reinit_sector_mode:
                    // we should wait
                    delay.QuadPart = -1000000; // 0.1 sec
                    KeDelayExecutionThread(KernelMode, FALSE, &delay);
                    // reinit sector mode
                    goto reinit_sector_mode;
/*
                    Vcb->LastModifiedTrack = 0;
                    UDFPrepareForWriteOperation(Vcb, Lba, BCount);
                    try_return(status = STATUS_SUCCESS);
*/
                }
            } else
            if((Vcb->CompatFlags & UDF_VCB_IC_BAD_RW_SEEK) &&
               (Vcb->IncrementalSeekState != INCREMENTAL_SEEK_DONE)) {
                UDFPrint(("Using incremental seek workaround...\n"));
                Vcb->IncrementalSeekState = INCREMENTAL_SEEK_WORKAROUND;
                try_return(status = STATUS_SUCCESS);
            } else {
                UDFPrint(("Seems to be BB @ %x\n", Lba));
                UpdateBB = TRUE;
            }
        } else
        if((Error->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST) &&
           (Error->AdditionalSenseCode == SCSI_ADSENSE_INVALID_SESSION_MODE)) {
            if(WriteOp &&
               (Vcb->SavedFeatures & CDRW_FEATURE_STREAMING) &&
               Lba+BCount <= Vcb->LastLBA+1) {
                UDFPrint(("bad Session in streaming mode. Lba %x, try fix-up\n", Lba));
                // ...flush device cache...
                UDFSyncCache(Vcb);
                // we should wait
                delay.QuadPart = -10000000; // 1 sec
                KeDelayExecutionThread(KernelMode, FALSE, &delay);
                try_return(status = STATUS_SUCCESS);
            }
        } else
        if((Error->LastError == CDRW_ERR_WRITE_IN_PROGRESS_BUSY) ||
           (status == STATUS_DEVICE_BUSY)) {
            delay.QuadPart = -5000000; // 0.5 sec
            UDFPrint(("CDRW_ERR_WRITE_IN_PROGRESS_BUSY || STATUS_DEVICE_BUSY\n"));
            KeDelayExecutionThread(KernelMode, FALSE, &delay);
#ifdef _UDF_STRUCTURES_H_
            if(Vcb->BGWriters) (*retry)++;
#endif //_UDF_STRUCTURES_H_
            try_return(status = STATUS_SUCCESS);
        } else
        // some devices (SONY) return such a strange sequence....
        if( ((Error->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST) &&
             (Error->AdditionalSenseCode == SCSI_ADSENSE_INVALID_CDB)) &&
              WriteOp) {
            // reinit write mode
            Vcb->LastModifiedTrack = 0;
            UDFPrepareForWriteOperation(Vcb, Lba, BCount);
            try_return(status = STATUS_SUCCESS);
        } else
        // No seek on Read... to morgue, I'm afraid
        if((Error->SenseKey == SCSI_SENSE_MEDIUM_ERROR) /*&&
           ((Error->AdditionalSenseCode == SCSI_ADSENSE_CD_READ_ERROR) ||
            (Error->AdditionalSenseCode == SCSI_ADSENSE_NO_SENSE) ||
            (Error->AdditionalSenseCode == SCSI_ADSENSE_FORMAT_CORRUPTED) ||
            (Error->AdditionalSenseCode == SCSI_ADSENSE_SEEK_ERROR))*/ &&
           !WriteOp) {
            if(Error->AdditionalSenseCode == SCSI_ADSENSE_SEEK_ERROR) {
                UDFPrint(("Seek error\n"));
                if(Vcb->CompatFlags & UDF_VCB_IC_BAD_RW_SEEK) {
                    UDFPrint(("try recovery\n"));
                    goto bad_rw_seek_recovery;
                }
                UDFPrint(("map error to STATUS_NONEXISTENT_SECTOR\n"));
                status = STATUS_NONEXISTENT_SECTOR;
            }
            UDFPrint(("Seems to be BB @ %x (read 2)\n", Lba));
            UpdateBB = TRUE;
        } else
        // handle invalid block address
        if( ((Error->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST) &&
             (Error->AdditionalSenseCode == SCSI_ADSENSE_ILLEGAL_BLOCK)) ) {
            if(!WriteOp &&
               (Vcb->SavedFeatures & CDRW_FEATURE_STREAMING) &&
               Lba+BCount <= Vcb->LastLBA+1) {
                UDFPrint(("bad LBA %x in streaming mode, try fix-up\n", Lba));
                // ...flush device cache...
                UDFSyncCache(Vcb);
                try_return(status = STATUS_SUCCESS);
            }

            if((Lba+BCount >= Vcb->LastLBA) &&
               (Vcb->MRWStatus == DiscInfo_BGF_Interrupted)) {
                UDFPrint(("stupid drive, cannot read beyond formatted area on DiscInfo_BGF_Interrupted\n"));
                UpdateBB = FALSE;
                try_return(status = STATUS_BUFFER_ALL_ZEROS);
            }
            // prevent Bad Block Bitmap modification
        }

try_exit: NOTHING;

    } _SEH2_FINALLY {
#ifdef UDF_DBG
        if(OS_SUCCESS(status)) {
            UDFPrint(("Retry\n"));
        }
#endif //UDF_DBG
    } _SEH2_END;
    if(!OS_SUCCESS(status)) {
        if((Vcb->MountPhErrorCount != (ULONG)-1) &&
           (Vcb->MountPhErrorCount < 0x7fffffff)) {
            Vcb->MountPhErrorCount++;
        }
//#ifdef _UDF_STRUCTURES_H_
        if(UpdateBB && (BCount == 1)) {
            uint32* bm;
            if(!(bm = (uint32*)(Vcb->BSBM_Bitmap))) {
                bm = (uint32*)(Vcb->BSBM_Bitmap = (int8*)DbgAllocatePoolWithTag(NonPagedPool, (i = (Vcb->LastPossibleLBA+1+7)>>3), 'mNWD' ));
                if(bm) {
                    RtlZeroMemory(bm, i);
                } else {
                    UDFPrint(("Can't alloc BSBM for %x blocks\n", Vcb->LastPossibleLBA));
                }
            }
            if(bm) {
                UDFSetBit(bm, Lba);
                UDFPrint(("Set BB @ %#x\n", Lba));
            }
#ifdef _BROWSE_UDF_
            bm = (uint32*)(Vcb->FSBM_Bitmap);
            if(bm) {
                UDFSetUsedBit(bm, Lba);
                UDFPrint(("Set BB @ %#x as used\n", Lba));
            }
#endif //_BROWSE_UDF_
        }
//#endif //_UDF_STRUCTURES_H_
    }
    return status;
} // end UDFRecoverFromError()

//#endif //_BROWSE_UDF_
/*
    This routine attempts to read disk layout using ReadDisk/Track info cmd
 */
OSSTATUS
UDFReadDiscTrackInfo(
    PDEVICE_OBJECT    DeviceObject,      // the target device object
    PVCB              Vcb                // Volume Control Block for ^ DevObj
    )
{
    OSSTATUS                    RC = STATUS_SUCCESS;
    PDISC_INFO_BLOCK_USER_OUT   DiscInfo = (PDISC_INFO_BLOCK_USER_OUT)MyAllocatePool__(NonPagedPool,sizeof(DISC_INFO_BLOCK_USER_OUT) );
    PTRACK_INFO_BLOCK_USER_OUT  TrackInfoOut = (PTRACK_INFO_BLOCK_USER_OUT)MyAllocatePool__(NonPagedPool,sizeof(TRACK_INFO_BLOCK_USER_OUT) );
    PTRACK_INFO_BLOCK_USER_IN   TrackInfoIn = (PTRACK_INFO_BLOCK_USER_IN)TrackInfoOut;
    READ_CAPACITY_USER_OUT      CapacityBuffer;
    LONG                        TrackNumber;
    BOOLEAN                     NotFP = FALSE;
    BOOLEAN                     ForceFP = FALSE;
    BOOLEAN                     PacketTrack = FALSE;
    BOOLEAN                     MRWRetry = FALSE;
//    BOOLEAN                     ReadCapacityOk = FALSE;
#ifdef UDF_FORMAT_MEDIA
    PUDFFmtState            fms = Vcb->fms;
#endif
    
    _SEH2_TRY {
        if(!DiscInfo || !TrackInfoOut)
            try_return(RC = STATUS_INSUFFICIENT_RESOURCES);

MRWRetry_label:

        RC = UDFPhSendIOCTL(IOCTL_CDRW_READ_DISC_INFO, DeviceObject,
                NULL, 0, 
                DiscInfo,sizeof(DISC_INFO_BLOCK_USER_OUT), TRUE, NULL);
        if(!OS_SUCCESS(RC)) {
            UDFPrint(("ReadDiskInfo failed. Use default.\n"));
            if(Vcb->MediaClassEx == CdMediaClass_DVDRW ||
                Vcb->MediaClassEx == CdMediaClass_DVDpRW ||
                Vcb->MediaClassEx == CdMediaClass_DVDRAM) {
                Vcb->LastPossibleLBA = DEFAULT_LAST_LBA_DVD;
            } else
            if(Vcb->MediaClassEx == CdMediaClass_BDRE) {
                Vcb->LastPossibleLBA = DEFAULT_LAST_LBA_BD;
            } else {
                Vcb->LastPossibleLBA = DEFAULT_LAST_LBA_FP_CD;
            }
            try_return(RC);
        }
#ifdef UDF_FORMAT_MEDIA
        if(fms && fms->opt_disk_info) {
            UserPrint(("ReadDiskInfo OK\n"));
        }
#endif //UDF_FORMAT_MEDIA

        RC = UDFPhSendIOCTL(IOCTL_CDRW_READ_CAPACITY, DeviceObject,
                NULL, 0, 
                &CapacityBuffer,sizeof(READ_CAPACITY_USER_OUT), TRUE, NULL);
        if(!OS_SUCCESS(RC)) {
            UDFPrint(("ReadCapacity failed.\n"));
            if(Vcb->MediaClassEx == CdMediaClass_DVDpRW) {
                Vcb->LastPossibleLBA = DEFAULT_LAST_LBA_DVD;
            }
        } else {
            UDFPrint(("ReadCapacity ok.\n"));
            UDFPrint(("Last possible LBA %#x.\n", CapacityBuffer.LogicalBlockAddress));
            if(!(CapacityBuffer.LogicalBlockAddress  & 0xc0000000) &&
                (CapacityBuffer.LogicalBlockAddress != 0x7fffffff)) {
                // good value from ReadCapacity
                UDFPrint(("Update Last possible LBA %#x.\n", CapacityBuffer.LogicalBlockAddress));
                Vcb->LastPossibleLBA = CapacityBuffer.LogicalBlockAddress;
//                ReadCapacityOk = TRUE;
#ifdef UDF_FORMAT_MEDIA
                if(fms && fms->opt_disk_info) {
                    UserPrint(("ReadCapacity OK\n"));
                }
#endif //UDF_FORMAT_MEDIA
            }
        }

#ifdef _CONSOLE
        Vcb->PhDeviceType = FILE_DEVICE_CD_ROM;
#endif //_CONSOLE
        Vcb->PhSerialNumber = *((uint32*)&(DiscInfo->DiskId));
        Vcb->PhErasable = DiscInfo->DiscStat.Flags & DiscInfo_Disk_Erasable;
        Vcb->PhDiskType = DiscInfo->DiskType;
        // save OPC info
        if(DiscInfo->OPCNum)
            Vcb->OPCNum = DiscInfo->OPCNum;
        UDFPrint(("DiskInfo: SN %x, OPCn %x(%x), Stat %x, Flg: %x\n",
            Vcb->PhSerialNumber, Vcb->OPCNum, DiscInfo->OPCNum, DiscInfo->DiscStat.Flags, DiscInfo->Flags.Flags));
#ifdef UDF_FORMAT_MEDIA
        if(fms && fms->opt_disk_info) {
            UserPrint(("Media type: "));
            switch(Vcb->MediaClassEx) {
            case CdMediaClass_CDROM     : UserPrint(("CD-ROM     \n")); break;
            case CdMediaClass_CDR       : UserPrint(("CD-R       \n")); break;
            case CdMediaClass_CDRW      : UserPrint(("CD-RW      \n")); break;
            case CdMediaClass_DVDROM    : UserPrint(("DVD-ROM    \n")); break;
            case CdMediaClass_DVDRAM    : UserPrint(("DVD-RAM    \n")); break;
            case CdMediaClass_DVDR      : UserPrint(("DVD-R      \n")); break;
            case CdMediaClass_DVDRW     : UserPrint(("DVD-RW     \n")); break;
            case CdMediaClass_DVDpR     : UserPrint(("DVD+R      \n")); break;
            case CdMediaClass_DVDpRW    : UserPrint(("DVD+RW     \n")); break;
            case CdMediaClass_DDCDROM   : UserPrint(("DDCD-ROM   \n")); break;
            case CdMediaClass_DDCDR     : UserPrint(("DDCD-R     \n")); break;
            case CdMediaClass_DDCDRW    : UserPrint(("DDCD-RW    \n")); break;
            case CdMediaClass_BDROM     : UserPrint(("BD-ROM     \n")); break;
            case CdMediaClass_BDRE      : UserPrint(("BD-RE      \n")); break;
            case CdMediaClass_BDR       : UserPrint(("BD-R       \n")); break;
            case CdMediaClass_HD_DVDROM : UserPrint(("HD DVD-ROM \n")); break;
            case CdMediaClass_HD_DVDRAM : UserPrint(("HD DVD-RAM \n")); break;
            case CdMediaClass_HD_DVDR   : UserPrint(("HD DVD-R   \n")); break;
            case CdMediaClass_HD_DVDRW  : UserPrint(("HD DVD-RW  \n")); break;
            default: UserPrint(("Unknown\n")); break;
            }
            UserPrint(("SN %#x, OPCn %#x\n",
                Vcb->PhSerialNumber, Vcb->OPCNum, DiscInfo->OPCNum));
            UserPrint(("Disk State: "));
            switch(DiscInfo->DiscStat.Flags & DiscInfo_Disk_Mask) {
            case DiscInfo_Disk_Empty:
                UserPrint(("Empty\n"));
                break;
            case DiscInfo_Disk_Appendable:
                UserPrint(("Appendable\n"));
                break;
            case DiscInfo_Disk_Complete:
                UserPrint(("Complete\n"));
                break;
            case DiscInfo_Disk_OtherRW:
                UserPrint(("RW in unknown state\n"));
                break;
            }
            UserPrint(("Last Session State: "));
            switch(DiscInfo->DiscStat.Flags & DiscInfo_Ses_Mask) {
            case DiscInfo_Ses_Empty:
                UserPrint(("Empty\n"));
                break;
            case DiscInfo_Ses_Incomplete:
                UserPrint(("Incomplete\n"));
                break;
            case DiscInfo_Ses_Complete:
                UserPrint(("Complete\n"));
                break;
            default:
                UserPrint(("unknown state\n"));
                break;
            }
            UserPrint(("Erasable: %s\n",
                (DiscInfo->DiscStat.Flags & DiscInfo_Disk_Erasable) ? "yes" : "no"
                ));
        }
#endif //UDF_FORMAT_MEDIA
        // Save disk status
        Vcb->DiscStat = DiscInfo->DiscStat.Flags;
        if((DiscInfo->DiscStat.Flags & DiscInfo_Disk_Mask) == DiscInfo_Disk_Empty) {
            UDFPrint(("Blank\n"));
            Vcb->BlankCD = TRUE;
        }
        if( (DiscInfo->DiscStat.Flags & DiscInfo_Disk_Mask) == DiscInfo_Disk_Empty ||
            (DiscInfo->DiscStat.Flags & DiscInfo_Ses_Mask) == DiscInfo_Ses_Incomplete) {
            // we shall mount empty disk to make it possible for
            // external applications to perform format operation
            // or something like this
            UDFPrint(("Try RAW_MOUNT\n"));
            Vcb->VCBFlags |= UDF_VCB_FLAGS_RAW_DISK;
            PacketTrack = TRUE;
        }

#ifndef _BROWSE_UDF_
        // If drive returned reasonable value from ReadCapacity, do not use
        // last LeadIn/LeadOut
        if(Vcb->MediaClassEx != CdMediaClass_DVDpRW &&
           !ReadCapacityOk) {
            // +RW returns bad value
            UDFPrint(("+RW returns bad value\n"));
            Vcb->LastPossibleLBA = (DiscInfo->LastSesLeadOutLBA & 0x80000000) ?
                0 : DiscInfo->LastSesLeadOutLBA;
            if(!(DiscInfo->LastSesLeadInLBA & 0x80000000)) {
                Vcb->LastPossibleLBA = max(DiscInfo->LastSesLeadInLBA, Vcb->LastPossibleLBA);
            }
        }
#endif // _BROWSE_UDF_
        if((DiscInfo->Flags.Flags & DiscInfo_BGF_Mask) != 0) {
            UDFPrint(("ForceFP + MRW\n"));
            ForceFP = TRUE;
            Vcb->MRWStatus = DiscInfo->Flags.Flags & DiscInfo_BGF_Mask;
            // update addressing mode
            if(!MRWRetry) {
                UDFSetMRWMode(Vcb);
                MRWRetry = TRUE;
                goto MRWRetry_label;
            }
        }
        UDFPrint(("MRW state %x\n", Vcb->MRWStatus));
        if(Vcb->MediaClassEx == CdMediaClass_DVDRW) {
            if(Vcb->PhMediaCapFlags & CdCapFlags_RandomWritable) {
                UDFPrint(("DVD-RW Rewritable\n"));
                ForceFP = TRUE;
            } else
            if((DiscInfo->DiscStat.Flags & DiscInfo_Disk_Mask) == DiscInfo_Disk_Empty) {
                UDFPrint(("Blank DVD-RW\n"));
                ForceFP = TRUE;
            } else {
                UDFPrint(("DVD-RW Sequential\n"));
                NotFP = TRUE;
            }
        } else
        if(CdrwIsDvdOverwritable(Vcb->MediaClassEx)) {
            UDFPrint(("force Rewritable (2)\n"));
            ForceFP = TRUE;
        }
        // We have incomplete last session, so process each track from last to first
//            Vcb->LastPossibleLBA = DiscInfo->LastSesLeadInLBA;

        Vcb->LastSession   = DiscInfo->Status.NumOfSes;
        Vcb->LastTrackNum  = DiscInfo->Status.LastTrackNumLastSes;
        Vcb->FirstTrackNum = DiscInfo->FirstTrackNum;             
        // some devices report LastTrackNum=0 for full disks
        Vcb->LastTrackNum = max(Vcb->LastTrackNum, Vcb->FirstTrackNum);
        if(!Vcb->LastTrackNum) {
            UDFPrint(("Try read 1st track...\n"));
            Vcb->LastTrackNum = 1;
        }
        UDFPrint(("DiskInfo: 1st trk %x, last trk %x\n", Vcb->FirstTrackNum, Vcb->LastTrackNum));
#ifdef UDF_FORMAT_MEDIA
        if(fms && fms->opt_disk_info) {
            UserPrint(("First track: %d\n"
                       "Last track:  %d\n", Vcb->FirstTrackNum, Vcb->LastTrackNum));
            UserPrint(("------------------------------------------\n"));
        }
#endif //UDF_FORMAT_MEDIA

        RC = UDFReallocTrackMap(Vcb, Vcb->LastTrackNum+1);
        if(!OS_SUCCESS(RC))
            try_return(RC);

        // Get last LBA from invisible track (if any)
        RtlZeroMemory(TrackInfoOut,sizeof(TRACK_INFO_BLOCK_USER_OUT));

        TrackInfoIn->LBA_TrkNum = 0; // invisible track
        TrackInfoIn->Track = TRUE;

        RC = UDFPhSendIOCTL(IOCTL_CDRW_READ_TRACK_INFO, DeviceObject,
                TrackInfoIn, sizeof(TRACK_INFO_BLOCK_USER_IN),
                TrackInfoOut,sizeof(TRACK_INFO_BLOCK_USER_OUT), TRUE, NULL);
        if(OS_SUCCESS(RC)) {
            if((Vcb->LastTrackNum < TrackInfoOut->TrackNum) &&
                TrackInfoOut->TrackLength &&
               (TrackInfoOut->TrackStartLBA != TrackInfoOut->NextWriteLBA)) {
                Vcb->LastTrackNum = TrackInfoOut->TrackNum;
                if(!(TrackInfoOut->NextWriteLBA & 0x80000000))
                    Vcb->NWA = TrackInfoOut->NextWriteLBA;
                if(TrackInfoOut->TrackLength > 1) {
                    Vcb->LastPossibleLBA =
                        TrackInfoOut->TrackStartLBA + TrackInfoOut->TrackLength - (TrackInfoOut->TrackLength ? 1 : 0);
                    UDFPrint((" set LastPossibleLBA=%x\n", Vcb->LastPossibleLBA));
                }
            }

            UDFPrint(("Ses %d, Track %d (%x, len %x) PckSize %x: \n"
                     "  NWA: %x (%s)  DatType:%x, %s %s %s %s TrkType:%x %s %s\n"
                     "  LRA: %x (%s)  RC_LBA:%x\n",
                TrackInfoOut->SesNum,
                0,
                TrackInfoOut->TrackStartLBA,
                TrackInfoOut->TrackLength,
                TrackInfoOut->FixPacketSize,

                TrackInfoOut->NextWriteLBA,
                TrackInfoOut->NWA_V & TrkInfo_NWA_V ? "vld" : "inv",
                TrackInfoOut->DataParam.Flags & TrkInfo_Dat_Mask,
                (TrackInfoOut->DataParam.Flags & TrkInfo_Packet) ? "Pck" : "",
                (TrackInfoOut->DataParam.Flags & TrkInfo_FP) ? "FP" : "",
                (TrackInfoOut->DataParam.Flags & TrkInfo_Blank) ? "Blank" : "",
                (TrackInfoOut->DataParam.Flags & TrkInfo_RT) ? "RT" : "",

                TrackInfoOut->TrackParam.Flags & TrkInfo_Trk_Mask,
                (TrackInfoOut->TrackParam.Flags & TrkInfo_Copy) ? "Cpy" : "",
                (TrackInfoOut->TrackParam.Flags & TrkInfo_Damage) ? "Damage" : "",

                TrackInfoOut->LastRecordedAddr,
                (TrackInfoOut->NWA_V & TrkInfo_LRA_V) ? "vld" : "inv",

                TrackInfoOut->ReadCompatLBA
                ));
#ifdef UDF_FORMAT_MEDIA
            if(fms && fms->opt_disk_info) {
                UserPrint(("Invisible track: \n"));
                UserPrint(("  Ses %d, Track %d (%x, len %x) PckSize %x: \n"
                           "    NWA: %x (%s)  DatType:%x, %s %s %s %s TrkType:%x %s %s\n"
                           "    LRA: %x (%s)  RC_LBA:%x\n",
                    TrackInfoOut->SesNum,
                    0,
                    TrackInfoOut->TrackStartLBA,
                    TrackInfoOut->TrackLength,
                    TrackInfoOut->FixPacketSize,

                    TrackInfoOut->NextWriteLBA,
                    TrackInfoOut->NWA_V & TrkInfo_NWA_V ? "vld" : "inv",
                    TrackInfoOut->DataParam.Flags & TrkInfo_Dat_Mask,
                    (TrackInfoOut->DataParam.Flags & TrkInfo_Packet) ? "Pck" : "",
                    (TrackInfoOut->DataParam.Flags & TrkInfo_FP) ? "FP" : "",
                    (TrackInfoOut->DataParam.Flags & TrkInfo_Blank) ? "Blank" : "",
                    (TrackInfoOut->DataParam.Flags & TrkInfo_RT) ? "RT" : "",

                    TrackInfoOut->TrackParam.Flags & TrkInfo_Trk_Mask,
                    (TrackInfoOut->TrackParam.Flags & TrkInfo_Copy) ? "Cpy" : "",
                    (TrackInfoOut->TrackParam.Flags & TrkInfo_Damage) ? "Damage" : "",

                    TrackInfoOut->LastRecordedAddr,
                    (TrackInfoOut->NWA_V & TrkInfo_LRA_V) ? "vld" : "inv",

                    TrackInfoOut->ReadCompatLBA
                    ));
            }
#endif //UDF_FORMAT_MEDIA

        }

        for (TrackNumber=(LONG)DiscInfo->FirstTrackNum;TrackNumber <= (LONG)Vcb->LastTrackNum;TrackNumber++) {

            RtlZeroMemory(TrackInfoOut,sizeof(TRACK_INFO_BLOCK_USER_OUT));
            TrackInfoIn->LBA_TrkNum = TrackNumber;
            TrackInfoIn->Track = TRUE;

            RC = UDFPhSendIOCTL(IOCTL_CDRW_READ_TRACK_INFO, DeviceObject,
                    TrackInfoIn, sizeof(TRACK_INFO_BLOCK_USER_IN),
                    TrackInfoOut,sizeof(TRACK_INFO_BLOCK_USER_OUT), TRUE, NULL);
            // fill sector type map
            if(TrackInfoOut->TrackStartLBA & 0x80000000) {
                UDFPrint(("TrkInfo: Bad FirstLba (%x), change to %x\n", TrackInfoOut->TrackStartLBA, 0));
                Vcb->TrackMap[TrackNumber].FirstLba = 0;
            } else {
                Vcb->TrackMap[TrackNumber].FirstLba = TrackInfoOut->TrackStartLBA;
            }
            if(TrackInfoOut->TrackLength & 0x80000000) {
                UDFPrint(("TrkInfo: Bad TrackLength (%x), change to %x\n", TrackInfoOut->TrackLength,
                    Vcb->LastPossibleLBA - Vcb->TrackMap[TrackNumber].FirstLba + 1));
                TrackInfoOut->TrackLength = Vcb->LastPossibleLBA - Vcb->TrackMap[TrackNumber].FirstLba + 1;
            }
            Vcb->TrackMap[TrackNumber].LastLba = TrackInfoOut->TrackStartLBA + 
                                                 TrackInfoOut->TrackLength - 
                                            (TrackInfoOut->TrackLength ? 1 : 0);

            Vcb->TrackMap[TrackNumber].TrackParam = TrackInfoOut->TrackParam.Flags;
            Vcb->TrackMap[TrackNumber].DataParam = TrackInfoOut->DataParam.Flags;
            Vcb->TrackMap[TrackNumber].NWA_V = TrackInfoOut->NWA_V;
            if((TrackInfoOut->NextWriteLBA & 0x80000000) || 
               (TrackInfoOut->NextWriteLBA < TrackInfoOut->TrackStartLBA)) {
                if(!(Vcb->TrackMap[TrackNumber].LastLba & 0x8000000)) {
                    UDFPrint(("TrkInfo: set NWA to LastLba (%x)\n", Vcb->TrackMap[TrackNumber].LastLba));
                    Vcb->TrackMap[TrackNumber].NWA =
                        Vcb->TrackMap[TrackNumber].LastLba;
                } else {
                    UDFPrint(("TrkInfo: set NWA to INV (1)\n"));
                    Vcb->TrackMap[TrackNumber].NWA = 0;
                    Vcb->TrackMap[TrackNumber].NWA_V = 0;
                }
            } else {
                if(!(TrackInfoOut->NextWriteLBA & 0x80000000)) {
                    UDFPrint(("TrkInfo: Good NWA (%x)\n", TrackInfoOut->NextWriteLBA));
                    Vcb->TrackMap[TrackNumber].NWA =
                        TrackInfoOut->NextWriteLBA;
                } else {
                    UDFPrint(("TrkInfo: set NWA to INV (2)\n"));
                    Vcb->TrackMap[TrackNumber].NWA = 0;
                    Vcb->TrackMap[TrackNumber].NWA_V = 0;
                }
            }
            Vcb->TrackMap[TrackNumber].Session = TrackInfoOut->SesNum;
            // for FP tracks we shall get PacketSize from returned info
            // otherwise set to default UDF value (0x20)
            if(NotFP) {
                UDFPrint(("Apply NotFP\n"));
                Vcb->TrackMap[TrackNumber].DataParam &= ~TrkInfo_FP;
#ifdef DBG
                TrackInfoOut->DataParam.Flags &= ~TrkInfo_FP;
#endif //DBG
            } else
            if(ForceFP) {
                UDFPrint(("Apply ForceFP\n"));
                PacketTrack = TRUE;
                Vcb->TrackMap[TrackNumber].DataParam |= TrkInfo_FP;
#ifdef DBG
                TrackInfoOut->DataParam.Flags |= TrkInfo_FP;
#endif //DBG
            }
            if(Vcb->TrackMap[TrackNumber].DataParam & TrkInfo_FP) {
                Vcb->TrackMap[TrackNumber].PacketSize = TrackInfoOut->FixPacketSize;
                Vcb->VCBFlags |= UDF_VCB_FLAGS_RAW_DISK;
                Vcb->FP_disc = TRUE;
            } else {
                Vcb->TrackMap[TrackNumber].PacketSize = PACKETSIZE_UDF;
            }
            // presence of Damaged track means, that we should mount this disk in RAW mode
            if(Vcb->TrackMap[TrackNumber].TrackParam & TrkInfo_Damage) {
                UDFPrint(("TrkInfo_Damage, Try RAW_MOUNT\n"));
                Vcb->VCBFlags |= UDF_VCB_FLAGS_RAW_DISK;
            }
            // presence of track with Unknown data type means, that we should mount
            // this disk in RAW mode
            if((TrackInfoOut->DataParam.Flags & TrkInfo_Dat_Mask) == TrkInfo_Trk_unknown) {
                UDFPrint(("Unknown DatType, Try RAW_MOUNT\n"));
                Vcb->VCBFlags |= UDF_VCB_FLAGS_RAW_DISK;
            }

            PacketTrack |= ((TrackInfoOut->DataParam.Flags & TrkInfo_Packet) != 0);

            UDFPrint(("Ses %d, Track %d (%x - %x) PckSize %x: \n"
                     "  NWA: %x (%s)  DatType:%x, %s %s %s %s TrkType:%x %s %s\n"
                     "  LRA: %x (%s)  RC_LBA:%x\n",
                TrackInfoOut->SesNum,
                TrackNumber,
                Vcb->TrackMap[TrackNumber].FirstLba,
                Vcb->TrackMap[TrackNumber].LastLba,
                TrackInfoOut->FixPacketSize,

                TrackInfoOut->NextWriteLBA,
                TrackInfoOut->NWA_V & TrkInfo_NWA_V ? "vld" : "inv",
                TrackInfoOut->DataParam.Flags & TrkInfo_Dat_Mask,
                (TrackInfoOut->DataParam.Flags & TrkInfo_Packet) ? "Pck" : "",
                (TrackInfoOut->DataParam.Flags & TrkInfo_FP) ? "FP" : "",
                (TrackInfoOut->DataParam.Flags & TrkInfo_Blank) ? "Blank" : "",
                (TrackInfoOut->DataParam.Flags & TrkInfo_RT) ? "RT" : "",

                TrackInfoOut->TrackParam.Flags & TrkInfo_Trk_Mask,
                (TrackInfoOut->TrackParam.Flags & TrkInfo_Copy) ? "Cpy" : "",
                (TrackInfoOut->TrackParam.Flags & TrkInfo_Damage) ? "Damage" : "",

                TrackInfoOut->LastRecordedAddr,
                (TrackInfoOut->NWA_V & TrkInfo_LRA_V) ? "vld" : "inv",

                TrackInfoOut->ReadCompatLBA
                ));
#ifdef UDF_FORMAT_MEDIA
            if(fms && fms->opt_disk_info) {
                UserPrint(("Track %d: \n", TrackNumber));
                UserPrint(("  Ses %d, Track %d (%x, len %x) PckSize %x: \n"
                           "    NWA: %x (%s)  DatType:%x, %s %s %s %s TrkType:%x %s %s\n"
                           "    LRA: %x (%s)  RC_LBA:%x\n",
                    TrackInfoOut->SesNum,
                    TrackNumber,
                    TrackInfoOut->TrackStartLBA,
                    TrackInfoOut->TrackLength,
                    TrackInfoOut->FixPacketSize,

                    TrackInfoOut->NextWriteLBA,
                    TrackInfoOut->NWA_V & TrkInfo_NWA_V ? "vld" : "inv",
                    TrackInfoOut->DataParam.Flags & TrkInfo_Dat_Mask,
                    (TrackInfoOut->DataParam.Flags & TrkInfo_Packet) ? "Pck" : "",
                    (TrackInfoOut->DataParam.Flags & TrkInfo_FP) ? "FP" : "",
                    (TrackInfoOut->DataParam.Flags & TrkInfo_Blank) ? "Blank" : "",
                    (TrackInfoOut->DataParam.Flags & TrkInfo_RT) ? "RT" : "",

                    TrackInfoOut->TrackParam.Flags & TrkInfo_Trk_Mask,
                    (TrackInfoOut->TrackParam.Flags & TrkInfo_Copy) ? "Cpy" : "",
                    (TrackInfoOut->TrackParam.Flags & TrkInfo_Damage) ? "Damage" : "",

                    TrackInfoOut->LastRecordedAddr,
                    (TrackInfoOut->NWA_V & TrkInfo_LRA_V) ? "vld" : "inv",

                    TrackInfoOut->ReadCompatLBA
                    ));
            }
#endif //UDF_FORMAT_MEDIA

            if(TrackNumber == DiscInfo->FirstTrackNum) {
                if(!(Vcb->TrackMap[TrackNumber].FirstLba & 0x80000000)) {
                    UDFPrint(("TrkInfo: Update FirstLBA (%x)\n", Vcb->TrackMap[TrackNumber].FirstLba));
                    Vcb->FirstLBA = Vcb->TrackMap[TrackNumber].FirstLba;
                }
            }
            if((TrackInfoOut->SesNum == Vcb->LastSession) && !Vcb->FirstTrackNumLastSes) {
                if(!(Vcb->TrackMap[TrackNumber].FirstLba & 0x80000000)) {
                    UDFPrint(("TrkInfo: Update FirstLBALastSes (%x)\n", Vcb->TrackMap[TrackNumber].FirstLba));
                    Vcb->FirstLBALastSes = Vcb->TrackMap[TrackNumber].FirstLba;
                }
                Vcb->FirstTrackNumLastSes = TrackNumber;
            }
        }

        if(!(TrackInfoOut->NextWriteLBA & 0x80000000) &&
           !(TrackInfoOut->TrackLength  & 0x80000000) &&
            (Vcb->NWA < TrackInfoOut->NextWriteLBA)
           ) {
            UDFPrint((" set NWA to %x\n", TrackInfoOut->NextWriteLBA));
            if(Vcb->MediaClassEx != CdMediaClass_DVDpRW) {
                Vcb->NWA = TrackInfoOut->NextWriteLBA;
            } else {
                Vcb->NWA = 
                    TrackInfoOut->TrackStartLBA + TrackInfoOut->TrackLength - (TrackInfoOut->TrackLength ? 1 : 0);
            }
        }
        if(Vcb->MediaClassEx != CdMediaClass_DVDpRW &&
           !(TrackInfoOut->TrackLength & 0x80000000) &&
           TrackInfoOut->TrackLength > 1) {
            Vcb->LastPossibleLBA =
                TrackInfoOut->TrackStartLBA + TrackInfoOut->TrackLength - (TrackInfoOut->TrackLength ? 1 : 0);
            UDFPrint((" set LastPossibleLBA=%x\n", Vcb->LastPossibleLBA));
        }
        TrackNumber = Vcb->LastTrackNum;
        // quick formatted +RW returns bogus value
        if(Vcb->MediaClassEx == CdMediaClass_DVDpRW) {
            UDFPrint((" check quick formatted +RW\n"));
            if(Vcb->TrackMap[TrackNumber].LastLba &&
               !(Vcb->TrackMap[TrackNumber].LastLba & 0x80000000) &&
               Vcb->TrackMap[TrackNumber].LastLba < Vcb->LastPossibleLBA /*&&
               Vcb->TrackMap[TrackNumber].LastLba != Vcb->LastPossibleLBA*/
               ) {
                UDFPrint((" track LastLBA %x != LastPossibleLBA %x, verify\n",
                    Vcb->TrackMap[TrackNumber].LastLba, Vcb->LastPossibleLBA));

                if(Vcb->MRWStatus == DiscInfo_BGF_Complete) {
                    UDFPrint((" complete MRW state\n"));
#ifdef _BROWSE_UDF_
                    Vcb->LastPossibleLBA =
                    Vcb->NWA = 
                    Vcb->LastLBA =
                    Vcb->TrackMap[TrackNumber].LastLba;
                    goto valid_track_length;
#endif // _BROWSE_UDF_
                } else
                if(Vcb->MRWStatus) {
                    uint8* buff;
                    SIZE_T ReadBytes;

                    UDFPrint((" MRW state %x\n", Vcb->MRWStatus));

                    buff = (uint8*)DbgAllocatePoolWithTag(NonPagedPool, Vcb->WriteBlockSize, 'bNWD' );
                    if(buff) {
                        RC = UDFTRead(Vcb,
                                       buff,
                                       Vcb->WriteBlockSize,
                                       Vcb->TrackMap[TrackNumber].LastLba+1,
                                       &ReadBytes,
                                       PH_TMP_BUFFER);
                        DbgFreePool(buff);
                        if(!OS_SUCCESS(RC)) {
                            UDFPrint((" Can't read beyond track LastLBA (%x)\n", Vcb->TrackMap[TrackNumber].LastLba+1));
                            Vcb->LastLBA = Vcb->TrackMap[TrackNumber].LastLba;
                            Vcb->NWA = Vcb->LastLBA+1;
                            Vcb->TrackMap[TrackNumber].NWA_V = 1;
                            Vcb->TrackMap[TrackNumber].NWA = Vcb->NWA;
                            Vcb->TrackMap[TrackNumber].LastLba = Vcb->LastPossibleLBA;
                            RC = STATUS_SUCCESS;
                            goto valid_track_length;
                        }
                    }
                }
            }
            UDFPrint((" set track LastLBA %x\n", Vcb->LastPossibleLBA));
            Vcb->NWA = 
            Vcb->LastLBA =
            Vcb->TrackMap[TrackNumber].LastLba =
                Vcb->LastPossibleLBA;
        }
valid_track_length:
        // Test for last empty session
        if((Vcb->TrackMap[TrackNumber].Session !=
            Vcb->TrackMap[TrackNumber-1].Session) &&
           (Vcb->LastSession > 1)) {
            // Note: some devices return negative track length
            if((Vcb->TrackMap[TrackNumber].LastLba <=  
                Vcb->TrackMap[TrackNumber].FirstLba) ||
               (Vcb->TrackMap[TrackNumber].FirstLba ==
                Vcb->TrackMap[TrackNumber].NWA)) {
                // empty last session...
                Vcb->LastTrackNum--;
//                TrackNumber--;
/*                for(SesNum = Vcb->TrackMap[TrackNumber].Session;
                    Vcb->TrackMap[TrackNumber].Session == SesNum;
                    TrackNumber--) {
                }*/
                if(TrackNumber>1)
                    Vcb->LastSession = Vcb->TrackMap[TrackNumber-1].Session;
            }
        }

        TrackNumber = Vcb->LastTrackNum;
#ifdef _BROWSE_UDF_
        Vcb->LastLBA = min(Vcb->TrackMap[TrackNumber].LastLba, Vcb->TrackMap[TrackNumber].NWA);
#endif //_BROWSE_UDF_

        if(Vcb->TrackMap[TrackNumber].NWA_V & TrkInfo_NWA_V) {
            UDFPrint((" NWA ok, set LastLBA to min(Last %x, NWA %x\n",
                Vcb->TrackMap[TrackNumber].LastLba,
                Vcb->TrackMap[TrackNumber].NWA));
            Vcb->LastLBA = min(Vcb->TrackMap[TrackNumber].LastLba, Vcb->TrackMap[TrackNumber].NWA);
        } else {
            UDFPrint((" no NWA, set LastLBA to Last %x\n", Vcb->TrackMap[TrackNumber].LastLba));
            Vcb->LastLBA = Vcb->TrackMap[TrackNumber].LastLba;
        }

        Vcb->VCBFlags |= UDF_VCB_FLAGS_TRACKMAP;
        if(!PacketTrack && Vcb->MediaClassEx != CdMediaClass_DVDRAM ) {
            UDFPrint((" disable Raw mount\n"));
            Vcb->VCBFlags &= ~UDF_VCB_FLAGS_RAW_DISK;
        }

try_exit:    NOTHING;

    } _SEH2_FINALLY {
        if(DiscInfo) MyFreePool__(DiscInfo);
        if(TrackInfoOut) MyFreePool__(TrackInfoOut);
    } _SEH2_END;

    return RC;
} // end UDFReadDiscTrackInfo()

/*
    This routine attempts to read disk layout using ReadFullTOC cmd
 */
OSSTATUS
UDFReadAndProcessFullToc(
    PDEVICE_OBJECT DeviceObject, // the target device object
    PVCB           Vcb
    )
{
    OSSTATUS                RC = STATUS_SUCCESS;
    PREAD_FULL_TOC_USER_OUT toc = (PREAD_FULL_TOC_USER_OUT)MyAllocatePool__(NonPagedPool,sizeof(READ_FULL_TOC_USER_OUT) );
    uint32 index;
    uint8 POINT;
    uint8 CurTrack = 0;
    uint32 LastLeadOut = 0;
//    BOOLEAN IsMRW = FALSE;

    UDFPrint(("UDFReadAndProcessFullToc\n"));

    if(!toc) return STATUS_INSUFFICIENT_RESOURCES;
    Vcb->FirstTrackNum = 0xFF;

    RtlZeroMemory(toc,sizeof(READ_FULL_TOC_USER_OUT));

    RC = UDFPhSendIOCTL(IOCTL_CDRW_READ_FULL_TOC,DeviceObject,
        NULL,0,
        toc,sizeof(READ_FULL_TOC_USER_OUT),
        TRUE,NULL);

    if(!OS_SUCCESS(RC)) {

        MyFreePool__(toc);
        return RC;
    }

#ifdef _CONSOLE
    Vcb->PhDeviceType = FILE_DEVICE_CD_ROM;
#endif //_CONSOLE
    Vcb->LastSession = toc->Sessions.Last_TrackSes;

    RC = UDFReallocTrackMap(Vcb, 0x100);
    if(!OS_SUCCESS(RC)) {
        MyFreePool__(toc);
        return RC;
    }

    // get LastPossibleLBA

    // Note: some drives return Full TOC items unordered.
    // So, LeadOut position may come before Track definition.
    // In order to handle such situation, we must initialize
    // CurTrack when First or Last Track descriptor comes
    for (index=0;(index<MAXIMUM_NUMBER_OF_SESSIONS);index++) {
/*        if((toc->SessionData[index].Adr == TOC_ADR_TrackInfo) &&
           ((toc->SessionData[index].Control == TOC_CTL_MRWTrackInfo) || (toc->SessionData[index].Control == TOC_CTL_MRWLastSes))) {
            IsMRW = TRUE;
        }*/
        if(toc->SessionData[index].Adr == 1) {
            switch (POINT = toc->SessionData[index].POINT) {
            case POINT_FirstTrackNum: {
                Vcb->FirstTrackNum = toc->SessionData[index].Params.FirstTrackNum.FirstTrackNum;
                if(!CurTrack)
                    CurTrack = (uint8)(Vcb->FirstTrackNum);
                break;
                }
            case POINT_LastTrackNum: {
                Vcb->LastTrackNum = toc->SessionData[index].Params.LastTrackNum.LastTrackNum;
                if(CurTrack < Vcb->LastTrackNum)
                    CurTrack = (uint8)(Vcb->FirstTrackNum);
                break;
                }
            case POINT_StartPositionOfLeadOut: {
#define TempMSF toc->SessionData[index].Params.StartPositionOfLeadOut.MSF
                Vcb->TrackMap[CurTrack].LastLba = MSF_TO_LBA(TempMSF[0],TempMSF[1],TempMSF[2]);
                LastLeadOut = max(LastLeadOut, Vcb->TrackMap[CurTrack].LastLba);
#undef TempMSF
                break;
                }
            default: {
                if( (Vcb->FirstTrackNum != 0x0FF) &&
                     (toc->SessionData[index].POINT == Vcb->FirstTrackNum) ) {
#define TempMSF toc->SessionData[index].Params.StartPositionOfTrack.MSF
                    Vcb->FirstLBA = MSF_TO_LBA(TempMSF[0],TempMSF[1],TempMSF[2]);
                    if(Vcb->FirstLBA & 0x80000000) {
                        Vcb->FirstLBA = 0;
                    }
#undef TempMSF
                }
                break;
                }
            }
            if((POINT >= POINT_StartPositionOfTrack_Min) &&
               (POINT <= POINT_StartPositionOfTrack_Max)) {
#define TempMSF toc->SessionData[index].Params.StartPositionOfTrack.MSF
                Vcb->TrackMap[POINT].FirstLba = MSF_TO_LBA(TempMSF[0],TempMSF[1],TempMSF[2])-1;
                if(Vcb->TrackMap[POINT].FirstLba & 0x80000000) {
                    if(POINT == 1) {
                        Vcb->TrackMap[POINT].FirstLba = 0;
                    } else {
                        if(Vcb->TrackMap[POINT-1].LastLba) {
                            Vcb->TrackMap[POINT].FirstLba = Vcb->TrackMap[POINT-1].LastLba+1;
                        }
                    }
                }
#undef TempMSF
                if(POINT > POINT_StartPositionOfTrack_Min) {
                    Vcb->TrackMap[POINT-1].LastLba = Vcb->TrackMap[POINT].FirstLba-1;
                }
                CurTrack = POINT;
            }
        } else
        if(toc->SessionData[index].Adr == 5) {
            switch (POINT = toc->SessionData[index].POINT) {
            case POINT_StartPositionOfNextProgramArea: {
#define TempMSF toc->SessionData[index].Params.StartPositionOfNextProgramArea.MaxLeadOut_MSF
                Vcb->LastPossibleLBA = MSF_TO_LBA(TempMSF[0],TempMSF[1],TempMSF[2]);
#undef TempMSF
                break;
                }
            default: {
                break;
                }
            }
        }

    }

/*    if(!IsMRW) {
        UDFPrint(("No MRW\n"));
        Vcb->CompatFlags &= ~UDF_VCB_IC_MRW_ADDR_PROBLEM;
    }*/
//        Vcb->CompatFlags &= ~UDF_VCB_IC_MRW_ADDR_PROBLEM;
    // some devices report LastTrackNum=0 for full disks
    Vcb->LastTrackNum = max(Vcb->LastTrackNum, Vcb->FirstTrackNum);
    Vcb->TrackMap[Vcb->LastTrackNum].LastLba = max(LastLeadOut, Vcb->TrackMap[Vcb->LastTrackNum].LastLba);

    Vcb->LastLBA = Vcb->TrackMap[Vcb->LastTrackNum].LastLba;

    MyFreePool__(toc);
//    Vcb->LastLBA=PacketVariable2Fixed(Vcb->LastLBA)-2;
    return STATUS_SUCCESS;
} // end UDFReadAndProcessFullToc()

/*
    use standard way to determine disk layout (ReadTOC cmd)
 */
OSSTATUS
UDFUseStandard(
    PDEVICE_OBJECT DeviceObject, // the target device object
    PVCB           Vcb           // Volume control block from this DevObj
    )
{
    OSSTATUS                RC = STATUS_SUCCESS;
    PREAD_TOC_USER_OUT      toc = (PREAD_TOC_USER_OUT)MyAllocatePool__(NonPagedPool,max(Vcb->BlockSize, sizeof(READ_TOC_USER_OUT)) );
    PGET_LAST_SESSION_USER_OUT LastSes = (PGET_LAST_SESSION_USER_OUT)MyAllocatePool__(NonPagedPool,sizeof(GET_LAST_SESSION_USER_OUT) );
    uint32                  LocalTrackCount;
//    uint32                  LocalTocLength;
    uint32                  TocEntry;
#ifdef _BROWSE_UDF_
    uint32                  OldTrkNum;
    uint32                  TrkNum;
    SIZE_T                  ReadBytes, i, len;
#endif //_BROWSE_UDF_
#ifdef UDF_FORMAT_MEDIA
    PUDFFmtState            fms = Vcb->fms;
#else
  #define fms FALSE
#endif //UDF_FORMAT_MEDIA

    UDFPrint(("UDFUseStandard\n"));

    _SEH2_TRY {

        if(!toc || !LastSes) {
            try_return (RC = STATUS_INSUFFICIENT_RESOURCES);
        }
        RtlZeroMemory(toc,sizeof(READ_TOC_TOC));

        Vcb->VCBFlags |= UDF_VCB_FLAGS_USE_STD;

        RC = UDFPhSendIOCTL(IOCTL_CDROM_READ_TOC,DeviceObject,
            toc,sizeof(READ_TOC_USER_OUT),
            toc,sizeof(READ_TOC_USER_OUT),
            TRUE,NULL );

        if((RC == STATUS_DEVICE_NOT_READY) || (RC == STATUS_NO_MEDIA_IN_DEVICE)) {
            try_return(RC);
        }
#ifdef UDF_FORMAT_MEDIA
        if(fms->opt_media == MT_none) {
            try_return(RC = STATUS_NO_MEDIA_IN_DEVICE);
        }
#endif //UDF_FORMAT_MEDIA
    
        // If even standard read toc does not work, then use default values
        if(!OS_SUCCESS(RC)) {

            RC = UDFReallocTrackMap(Vcb, 2);
            if(!OS_SUCCESS(RC)) {
                try_return(RC);
            }

            Vcb->LastSession=1;
            Vcb->FirstTrackNum=1;
//            Vcb->FirstLBA=0;
            Vcb->LastTrackNum=1;
            Vcb->TrackMap[1].FirstLba = Vcb->FirstLBA;
            Vcb->TrackMap[1].LastLba = Vcb->LastLBA;
            Vcb->TrackMap[1].PacketSize = PACKETSIZE_UDF;
#ifdef UDF_FORMAT_MEDIA
            if(!fms) {
#endif //UDF_FORMAT_MEDIA

#ifdef _BROWSE_UDF_
#ifdef UDF_HDD_SUPPORT
                if(UDFGetDevType(DeviceObject) == FILE_DEVICE_DISK) {
                    try_return(RC = STATUS_SUCCESS);
                }
#endif //UDF_HDD_SUPPORT
#endif //_BROWSE_UDF_

#ifdef UDF_FORMAT_MEDIA
            } else {

                if(fms->opt_media == MT_HD) {
                    Vcb->LastPossibleLBA = Vcb->LastLBA;
                    try_return(RC = STATUS_SUCCESS);
                }
            }
#endif //UDF_FORMAT_MEDIA
            Vcb->LastPossibleLBA = max(Vcb->LastLBA, DEFAULT_LAST_LBA_FP_CD);
            Vcb->TrackMap[1].DataParam = TrkInfo_Dat_XA | TrkInfo_FP | TrkInfo_Packet;
            Vcb->TrackMap[1].TrackParam = TrkInfo_Trk_XA;
            Vcb->TrackMap[1].NWA = 0xffffffff;
            Vcb->NWA = DEFAULT_LAST_LBA_FP_CD + 7 + 1;
            try_return(RC = STATUS_SUCCESS);
        }

#ifdef _CONSOLE
        Vcb->PhDeviceType = FILE_DEVICE_CD_ROM;
#endif //_CONSOLE
    
        LocalTrackCount = toc->Tracks.Last_TrackSes - toc->Tracks.First_TrackSes + 1;
//        LocalTocLength = PtrOffset( toc, &(toc->TrackData[LocalTrackCount + 1]) );  /* FIXME ReactOS Assume PtrOffset is not changing it's arguments? */
      
        // Get out if there is an immediate problem with the TOC.
        if(toc->Tracks.First_TrackSes > toc->Tracks.Last_TrackSes) {
            try_return(RC = STATUS_DISK_CORRUPT_ERROR);
        }

#ifdef _BROWSE_UDF_        
        Vcb->LastTrackNum=toc->Tracks.Last_TrackSes;
        Vcb->FirstTrackNum=toc->Tracks.First_TrackSes;
        // some devices report LastTrackNum=0 for full disks
        Vcb->LastTrackNum = max(Vcb->LastTrackNum, Vcb->FirstTrackNum);

        RC = UDFReallocTrackMap(Vcb, MAXIMUM_NUMBER_OF_TRACKS+1);
/*        if(Vcb->TrackMap) {
            MyFreePool__(Vcb->TrackMap);
            Vcb->TrackMap = NULL;
        }
        Vcb->TrackMap = (PUDFTrackMap)
            MyAllocatePool__(NonPagedPool, (MAXIMUM_NUMBER_OF_TRACKS+1)*sizeof(UDFTrackMap));
        if(!Vcb->TrackMap) {
            MyFreePool__(toc);
            return STATUS_INSUFFICIENT_RESOURCES;
        }
        RtlZeroMemory(Vcb->TrackMap,(MAXIMUM_NUMBER_OF_TRACKS+1)*sizeof(UDFTrackMap));
*/
        if(!OS_SUCCESS(RC)) {
            BrutePoint();
            try_return(RC);
        }
        // find 1st and last session
        RC = UDFPhSendIOCTL(IOCTL_CDROM_GET_LAST_SESSION,DeviceObject,
            LastSes,sizeof(GET_LAST_SESSION_USER_OUT),
            LastSes,sizeof(GET_LAST_SESSION_USER_OUT),
            TRUE,NULL );

        if(OS_SUCCESS(RC)) {
            TrkNum = LastSes->LastSes_1stTrack.TrackNum;
            Vcb->LastSession = LastSes->Sessions.First_TrackSes;
            for(TocEntry=0;TocEntry<LocalTrackCount + 1;TocEntry++) {
                if(toc->TrackData[TocEntry].TrackNum == TrkNum) {
                    Vcb->TrackMap[TrkNum].Session = Vcb->LastSession;
                }
            }
        }
    
        OldTrkNum = 0;
        // Scan toc for first & last LBA
        for(TocEntry=0;TocEntry<LocalTrackCount + 1;TocEntry++) {
#define TempMSF toc->TrackData[TocEntry].LBA
            TrkNum = toc->TrackData[TocEntry].TrackNum;
#ifdef UDF_DBG
            if (TrkNum >= MAXIMUM_NUMBER_OF_TRACKS &&
                TrkNum != TOC_LastTrack_ID) {
                UDFPrint(("UDFUseStandard: Array out of bounds\n"));
                BrutePoint();
                try_return(RC = STATUS_SUCCESS);
            }
            UDFPrint(("Track N %d (0x%x) first LBA %ld (%lx) \n",TrkNum,TrkNum,
                MSF_TO_LBA(TempMSF[1],TempMSF[2],TempMSF[3]),
                MSF_TO_LBA(TempMSF[1],TempMSF[2],TempMSF[3])));
#endif // UDF_DBG
            if(Vcb->FirstTrackNum == TrkNum) {
                Vcb->FirstLBA = MSF_TO_LBA(TempMSF[1],TempMSF[2],TempMSF[3]);
                if(Vcb->FirstLBA & 0x80000000) {
                    Vcb->FirstLBA = 0;
                }
            }
            if(TOC_LastTrack_ID   == TrkNum) {
                Vcb->LastLBA  = MSF_TO_LBA(TempMSF[1],TempMSF[2],TempMSF[3])-1;
                Vcb->TrackMap[OldTrkNum].LastLba = Vcb->LastLBA-1;
                UDFPrint(("UDFUseStandard: Last track entry, break TOC scan\n"));
//                continue;
                break;
            } else {
                Vcb->TrackMap[TrkNum].FirstLba = MSF_TO_LBA(TempMSF[1],TempMSF[2],TempMSF[3]);
                if(Vcb->TrackMap[TrkNum].FirstLba & 0x80000000)
                    Vcb->TrackMap[TrkNum].FirstLba = 0;
                if(TrkNum) {
                    if (TOC_LastTrack_ID == OldTrkNum) {
                        UDFPrint(("UDFUseStandard: Wrong previous track number\n"));
                        BrutePoint();
                    } else {
                        Vcb->TrackMap[OldTrkNum].LastLba = Vcb->TrackMap[TrkNum].FirstLba-1;
                    }
                }
            }
            // check track type
            switch(toc->TrackData[TocEntry].Control & TocControl_TrkMode_Mask) {
            case TocControl_TrkMode_Data:
            case TocControl_TrkMode_IncrData:
                Vcb->TrackMap[TrkNum].DataParam = TrkInfo_Dat_XA;
                Vcb->TrackMap[TrkNum].TrackParam = TrkInfo_Trk_XA;
                break;
            default:
                Vcb->TrackMap[TrkNum].DataParam = TrkInfo_Dat_unknown;
                Vcb->TrackMap[TrkNum].TrackParam = TrkInfo_Trk_unknown;
            }
            OldTrkNum = TrkNum;
#undef TempMSF
        }

        TrkNum = Vcb->LastTrackNum;
        RC = STATUS_SUCCESS;
        // find last _valid_ track
        for(;TrkNum;TrkNum--) {
            if((Vcb->TrackMap[TrkNum].DataParam  != TrkInfo_Dat_unknown) &&
               (Vcb->TrackMap[TrkNum].TrackParam != TrkInfo_Trk_unknown)) {
                RC = STATUS_UNSUCCESSFUL;
                Vcb->LastTrackNum = TrkNum;
                break;
            }
        }
        // no valid tracks...
        if(!TrkNum) {
            UDFPrint(("UDFUseStandard: no valid tracks...\n"));
            try_return(RC = STATUS_UNRECOGNIZED_VOLUME);
        }
        i = 0;

        // Check for last VP track. Some last sectors may belong to Link-data &
        // be unreadable. We should forget about them, because UDF needs
        // last _readable_ sector.
        while(!OS_SUCCESS(RC) && (i<8)) {
            RC = UDFPhReadSynchronous(Vcb->TargetDeviceObject, (int8*)toc, Vcb->BlockSize,
                       ((uint64)(Vcb->TrackMap[TrkNum].LastLba-i)) << Vcb->BlockSizeBits, &ReadBytes, PH_TMP_BUFFER);
            i++;
        }
        if(OS_SUCCESS(RC)) {
            Vcb->LastLBA = Vcb->TrackMap[TrkNum].LastLba-i+1;
/*            if(i) {
                Vcb->TrackMap[TrkNum].PacketSize = PACKETSIZE_UDF;
                Vcb->TrackMap[TrkNum].;
            }*/
        } else {

            // Check for FP track. READ_TOC reports actual track length, but
            // Link-data is hidden & unreadable for us. So, available track
            // length may be less than actual. Here we assume that Packet-size
            // is PACKETSIZE_UDF.
            i = 0;
            len = Vcb->TrackMap[TrkNum].LastLba - Vcb->TrackMap[TrkNum].FirstLba + 1;
            len = (uint32)(((int64)len*PACKETSIZE_UDF) / (PACKETSIZE_UDF+7));

            while(!OS_SUCCESS(RC) && (i<9)) {
                RC = UDFPhReadSynchronous(Vcb->TargetDeviceObject, (int8*)toc, Vcb->BlockSize,
                           ((uint64)(Vcb->TrackMap[TrkNum].FirstLba-i+len)) << Vcb->BlockSizeBits, &ReadBytes, PH_TMP_BUFFER);
                i++;
            }
            if(OS_SUCCESS(RC)) {
                Vcb->LastLBA =
                Vcb->TrackMap[TrkNum].LastLba = Vcb->TrackMap[TrkNum].FirstLba-i+len+1;
                Vcb->TrackMap[TrkNum].PacketSize = PACKETSIZE_UDF;
//                Vcb->TrackMap[TrkNum].;
            } else
            if(RC == STATUS_INVALID_DEVICE_REQUEST) {
                // wrap return code from Audio-disk
                RC = STATUS_SUCCESS;
            }
        }

#ifdef UDF_CDRW_EMULATION_ON_ROM
        Vcb->LastPossibleLBA = Vcb->LastLBA+7+1+1024;
        Vcb->NWA = Vcb->LastLBA+7+1;
#else
        Vcb->LastPossibleLBA =
        Vcb->NWA = Vcb->LastLBA+7+1;
#endif //UDF_CDRW_EMULATION_ON_ROM

#else //_BROWSE_UDF_

        Vcb->FirstTrackNum=toc->Tracks.Last_TrackSes;
        Vcb->LastTrackNum=toc->Tracks.First_TrackSes;
    
        // Scan toc for first & last LBA
        for(TocEntry=0;TocEntry<LocalTrackCount + 1;TocEntry++) {
#define TempMSF toc->TrackData[TocEntry].LBA
            if(Vcb->FirstTrackNum == toc->TrackData[TocEntry].TrackNum) {
                Vcb->FirstLBA = MSF_TO_LBA(TempMSF[1],TempMSF[2],TempMSF[3]);
                if(Vcb->FirstLBA & 0x80000000) {
                    Vcb->FirstLBA = 0;
                }
            }
            if(TOC_LastTrack_ID   == toc->TrackData[TocEntry].TrackNum) {
                Vcb->LastLBA = MSF_TO_LBA(TempMSF[1],TempMSF[2],TempMSF[3])-1;
            }
#undef TempMSF
        }
    
//        Vcb->LastLBA=PacketVariable2Fixed(Vcb->LastLBA)-2;
        Vcb->LastPossibleLBA = DEFAULT_LAST_LBA_FP_CD;
#endif //_BROWSE_UDF_
try_exit: NOTHING;
    } _SEH2_FINALLY {
        if(toc) MyFreePool__(toc);
        if(LastSes) MyFreePool__(LastSes);
    } _SEH2_END;

    return RC;
} // end UDFUseStandard()

/*
    Get block size (for read operation)
 */
OSSTATUS
UDFGetBlockSize(
    IN PDEVICE_OBJECT DeviceObject,      // the target device object
    IN PVCB           Vcb                // Volume control block from this DevObj
    )
{
    OSSTATUS        RC = STATUS_SUCCESS;
    PDISK_GEOMETRY  DiskGeometry = (PDISK_GEOMETRY)MyAllocatePool__(NonPagedPool,sizeof(DISK_GEOMETRY));
    PPARTITION_INFORMATION  PartitionInfo = (PPARTITION_INFORMATION)MyAllocatePool__(NonPagedPool,sizeof(PARTITION_INFORMATION)*2);
#ifdef UDF_FORMAT_MEDIA
    PUDFFmtState            fms = Vcb->fms;
#else
  #define fms FALSE
#endif //UDF_FORMAT_MEDIA

    if(!DiskGeometry || !PartitionInfo)
        try_return (RC = STATUS_INSUFFICIENT_RESOURCES);

#ifdef _BROWSE_UDF_

#ifdef UDF_HDD_SUPPORT
    if(!fms) {
        if(UDFGetDevType(DeviceObject) == FILE_DEVICE_DISK) {
            UDFPrint(("UDFGetBlockSize: HDD\n"));
            RC = UDFPhSendIOCTL(IOCTL_DISK_GET_DRIVE_GEOMETRY,DeviceObject,
                0,NULL,
                DiskGeometry,sizeof(DISK_GEOMETRY),
                TRUE,NULL );
            Vcb->BlockSize = (OS_SUCCESS(RC)) ? DiskGeometry->BytesPerSector : 512;
            if(!NT_SUCCESS(RC))
                try_return(RC);
            RC = UDFPhSendIOCTL(IOCTL_DISK_GET_PARTITION_INFO,DeviceObject,
                0,NULL,
                PartitionInfo,sizeof(PARTITION_INFORMATION),
                TRUE,NULL );
            if(!NT_SUCCESS(RC)) {
                UDFPrint(("UDFGetBlockSize: IOCTL_DISK_GET_PARTITION_INFO failed\n"));
                if(RC == STATUS_INVALID_DEVICE_REQUEST) /* ReactOS Code Change (was =) */
                    RC = STATUS_UNRECOGNIZED_VOLUME;
                try_return(RC);
            }
            if(PartitionInfo->PartitionType != PARTITION_IFS) {
                UDFPrint(("UDFGetBlockSize: PartitionInfo->PartitionType != PARTITION_IFS\n"));
                try_return(RC = STATUS_UNRECOGNIZED_VOLUME);
            }
        } else {
#endif //UDF_HDD_SUPPORT
            RC = UDFPhSendIOCTL(IOCTL_CDROM_GET_DRIVE_GEOMETRY,DeviceObject,
                DiskGeometry,sizeof(DISK_GEOMETRY),
                DiskGeometry,sizeof(DISK_GEOMETRY),
                TRUE,NULL );

            if(RC == STATUS_DEVICE_NOT_READY) {
                // probably, the device is really busy, may be by CD/DVD recording
                UserPrint(("  busy (0)\n"));
                try_return(RC);
            }

            Vcb->BlockSize = (OS_SUCCESS(RC)) ? DiskGeometry->BytesPerSector : 2048;
#ifdef UDF_HDD_SUPPORT
        }
    }
#endif //UDF_HDD_SUPPORT

#endif //_BROWSE_UDF_

#ifdef UDF_FORMAT_MEDIA
    if(fms) {
        RC = UDFPhSendIOCTL(IOCTL_CDROM_GET_DRIVE_GEOMETRY,DeviceObject,
            DiskGeometry,sizeof(DISK_GEOMETRY),
            DiskGeometry,sizeof(DISK_GEOMETRY),
            FALSE, NULL );

        if(!NT_SUCCESS(RC)) {
            RC = UDFPhSendIOCTL(IOCTL_DISK_GET_DRIVE_GEOMETRY,DeviceObject,
                DiskGeometry,sizeof(DISK_GEOMETRY),
                DiskGeometry,sizeof(DISK_GEOMETRY),
                FALSE, NULL );
            if(NT_SUCCESS(RC)) {
                fms->opt_media = MT_HD;
                RC = UDFPhSendIOCTL(IOCTL_DISK_GET_PARTITION_INFO,DeviceObject,
                    NULL,0,
                    PartitionInfo,sizeof(PARTITION_INFORMATION)*2,
                    FALSE, NULL );
                if(!NT_SUCCESS(RC)) {
                    LONG HiOffs=0;
                    RC = SetFilePointer(DeviceObject->h,0,&HiOffs,FILE_END);
                }
            }
        }

        if(RC == STATUS_DEVICE_NOT_READY) {
            // probably, the device is really busy, may be by CD/DVD recording
            UserPrint(("  busy\n"));
            try_return(RC );
        }

        Vcb->BlockSize = (NT_SUCCESS(RC)) ? DiskGeometry->BytesPerSector : 2048;
    }
#endif //UDF_FORMAT_MEDIA

    // Block size must be an even multiple of 512
    switch (Vcb->BlockSize) {
        case 2048: Vcb->BlockSizeBits = 11; break;
#ifdef UDF_HDD_SUPPORT
        case 512:  Vcb->BlockSizeBits = 9; break;
        case 1024: Vcb->BlockSizeBits = 10; break;
        case 4096: Vcb->BlockSizeBits = 12; break;
        case 8192: Vcb->BlockSizeBits = 13; break;
#endif //UDF_HDD_SUPPORT
        default:
        {
            UserPrint(("UDF: Bad block size (%ld)\n", Vcb->BlockSize));
            try_return(RC = STATUS_UNSUCCESSFUL);
        }
    }

#ifdef UDF_HDD_SUPPORT
    if(
#ifdef _BROWSE_UDF_
        (!fms && (UDFGetDevType(DeviceObject) == FILE_DEVICE_DISK))
                         ||
#endif //_BROWSE_UDF_
#ifdef UDF_FORMAT_MEDIA
        (fms && fms->opt_media == MT_HD)
                         ||
#endif //UDF_FORMAT_MEDIA
         FALSE ) {

#ifdef UDF_FORMAT_MEDIA
        if(fms && !NT_SUCCESS(RC))
            try_return(STATUS_UNSUCCESSFUL);
#endif //UDF_FORMAT_MEDIA

        Vcb->FirstLBA=0;//(ULONG)(PartitionInfo->StartingOffset.QuadPart >> Vcb->BlockSizeBits);
        Vcb->LastPossibleLBA =
        Vcb->LastLBA = (uint32)(PartitionInfo->PartitionLength.QuadPart >> Vcb->BlockSizeBits)/* + Vcb->FirstLBA*/ - 1;
    } else {
#endif //UDF_HDD_SUPPORT
        Vcb->FirstLBA=0;
        if(OS_SUCCESS(RC)) {
            Vcb->LastLBA = (uint32)(DiskGeometry->Cylinders.QuadPart *
                                    DiskGeometry->TracksPerCylinder *
                                    DiskGeometry->SectorsPerTrack - 1);
            if(Vcb->LastLBA == 0x7fffffff) {
                Vcb->LastLBA = UDFIsDvdMedia(Vcb) ? DEFAULT_LAST_LBA_DVD : DEFAULT_LAST_LBA_FP_CD;
            }
        } else {
            Vcb->LastLBA = UDFIsDvdMedia(Vcb) ? DEFAULT_LAST_LBA_DVD : DEFAULT_LAST_LBA_FP_CD;
        }
        Vcb->LastPossibleLBA = Vcb->LastLBA;
#ifdef UDF_HDD_SUPPORT
    }
#endif //UDF_HDD_SUPPORT

#ifdef _BROWSE_UDF_
//    if(UDFGetDevType(DeviceObject) == FILE_DEVICE_DISK) {
        Vcb->WriteBlockSize = PACKETSIZE_UDF*Vcb->BlockSize;
//    } else {
//        Vcb->WriteBlockSize = PACKETSIZE_UDF*Vcb->BlockSize;
//    }
#else //_BROWSE_UDF_
    if(fms->opt_media == MT_HD) {
        Vcb->WriteBlockSize = Vcb->BlockSize;
    } else {
        Vcb->WriteBlockSize = PACKETSIZE_UDF*Vcb->BlockSize;
    }
#endif //_BROWSE_UDF_

    RC = STATUS_SUCCESS;

try_exit:   NOTHING;

    UDFPrint(("UDFGetBlockSize:\nBlock size is %x, Block size bits %x, Last LBA is %x\n",
              Vcb->BlockSize, Vcb->BlockSizeBits, Vcb->LastLBA));

    MyFreePool__(PartitionInfo);
    MyFreePool__(DiskGeometry);
    return RC;

} // end UDFGetBlockSize()

#ifdef _BROWSE_UDF_

OSSTATUS
UDFCheckTrackFPAddressing(
//    IN PDEVICE_OBJECT DeviceObject,      // the target device object
    IN PVCB           Vcb,               // Volume control block from this DevObj
    IN ULONG          TrackNum
    )
{
    OSSTATUS RC = STATUS_SUCCESS;
//    OSSTATUS RC2 = STATUS_UNSUCCESSFUL;
    uint32 lba=0;
    uint32 i;
    uint8* Buffer;
//    SIZE_T ReadBytes;

    uint8  user_data;

    ULONG FirstChunkLen = 0;

    ULONG NextChunkLen = 0;
    ULONG NextChunkLenCount = 0;

    ULONG NextChunkLenOth = 0;
    ULONG NextChunkLenOthCount = 0;
//    ULONG MRW_Offset = 0;

    PLL_READ_USER_IN pLLR_in;
    PCD_SECTOR_HEADER pHdr;
/*    uint8 cMSF[3] = {0,2,0};
    uint8 cMSF1[3] = {0,2,1};*/


    if(!Vcb->TrackMap) {
        Vcb->CompatFlags &= ~UDF_VCB_IC_FP_ADDR_PROBLEM;
        return STATUS_SUCCESS;
    }

    Buffer = (uint8*)DbgAllocatePoolWithTag(NonPagedPool, max(Vcb->BlockSize,
                                                     sizeof(LL_READ_USER_IN)+16), 'pNWD');
    if(!Buffer)
        return STATUS_INSUFFICIENT_RESOURCES;
    pLLR_in = (PLL_READ_USER_IN)Buffer;
    pHdr = (PCD_SECTOR_HEADER)(Buffer+sizeof(LL_READ_USER_IN));

/*    if(Vcb->CompatFlags & UDF_VCB_IC_MRW_ADDR_PROBLEM) {
        MRW_Offset = (MRW_DMA_OFFSET/32)*39;
    }*/

    user_data = 0;
    for(i=0; i<=0x200; i++) {

        RtlZeroMemory(pLLR_in, sizeof(pLLR_in)+16);
        pLLR_in->ExpectedBlkType = ReadCd_BlkType_Any;
        pLLR_in->LBA = i;
        pLLR_in->NumOfBlocks = 1;
        pLLR_in->Flags.Flags = ReadCd_Header_Hdr;
//        pLLR_in->UseMFS = FALSE; // already zero
//        MOV_MSF(pLLR_in->Starting_MSF, cMSF);
//        MOV_MSF(pLLR_in->Ending_MSF, cMSF1);
        RtlZeroMemory(pHdr, sizeof(CD_SECTOR_HEADER));
        RC = UDFPhSendIOCTL(IOCTL_CDRW_LL_READ, Vcb->TargetDeviceObject,
            pLLR_in, sizeof(LL_READ_USER_IN),
            pHdr, sizeof(CD_SECTOR_HEADER),
            TRUE, NULL );

/*        RC = UDFPhReadSynchronous(Vcb->TargetDeviceObject, Buffer, Vcb->BlockSize,
                   ((uint64)(i+MRW_Offset)) << Vcb->BlockSizeBits, &ReadBytes, 0);*/

        // skip unreadable
        if(!OS_SUCCESS(RC)) {
            UDFPrint(("  Read error at lba %x\n", i));
            continue;
        }

        // skip strange (damaged ?) blocks
        if((pHdr->Mode.Flags & WParam_SubHdr_Mode_Mask) != WParam_SubHdr_Mode1 &&
           (pHdr->Mode.Flags & WParam_SubHdr_Mode_Mask) != WParam_SubHdr_Mode2) {
            UDFPrint(("  Unexpected data type (%x) at lba %x\n", pHdr->Mode.Flags & WParam_SubHdr_Mode_Mask, i));
            continue;
        }

        if((pHdr->Mode.Flags & WParam_SubHdr_Format_Mask) == WParam_SubHdr_Format_UserData &&
            !user_data) {
            lba = i;
        }

/*        if(OS_SUCCESS(RC) && !OS_SUCCESS(RC2)) {
            lba = i;
        }*/

        if((pHdr->Mode.Flags & WParam_SubHdr_Format_Mask) != WParam_SubHdr_Format_UserData &&
            user_data) {
//        if(!OS_SUCCESS(RC) && OS_SUCCESS(RC2)) {
            UDFPrint(("  %x - %x (%x sectors)\n", lba, i-1, i-lba));
            if(!FirstChunkLen) {
                FirstChunkLen = i-lba;
            } else {
                if(!NextChunkLen) {
                    NextChunkLen = i-lba;
                    NextChunkLenCount++;
                } else {
                    if(NextChunkLen == i-lba) {
                        NextChunkLenCount++;
                    } else {
                        if((NextChunkLenOth+1) % (NextChunkLen+1)) {
                            NextChunkLenOth = i-lba;
                            NextChunkLenOthCount++;
                        } else {
                            NextChunkLenCount++;
                        }
                    }
                }
            }
        }
        user_data = ((pHdr->Mode.Flags & WParam_SubHdr_Format_Mask) == WParam_SubHdr_Format_UserData);
//        RC2 = RC;
    }

    DbgFreePool(Buffer);

    if(!NextChunkLenCount && !NextChunkLenOthCount) {
        Vcb->CompatFlags &= ~UDF_VCB_IC_FP_ADDR_PROBLEM;
        return STATUS_SUCCESS;
    }
    if(NextChunkLenOthCount > NextChunkLenCount) {
        NextChunkLen = NextChunkLenOth;
    }
    if(NextChunkLen > PACKETSIZE_UDF+7) {
        Vcb->CompatFlags &= ~UDF_VCB_IC_FP_ADDR_PROBLEM;
        return STATUS_SUCCESS;
    }
    Vcb->TrackMap[TrackNum].DataParam &= ~TrkInfo_Dat_Mask;
    Vcb->TrackMap[TrackNum].DataParam |= TrkInfo_Dat_XA;
    Vcb->TrackMap[TrackNum].Flags |= TrackMap_FixFPAddressing;
    Vcb->TrackMap[TrackNum].PacketSize = 1;
    while(NextChunkLen >> Vcb->TrackMap[TrackNum].PacketSize) {
        Vcb->TrackMap[TrackNum].PacketSize++;
    }
    Vcb->TrackMap[TrackNum].PacketSize = 1 << (Vcb->TrackMap[TrackNum].PacketSize-1);
    Vcb->TrackMap[TrackNum].TrackFPOffset = NextChunkLen - FirstChunkLen;  // !!!!!
    Vcb->TrackMap[TrackNum].PacketFPOffset = Vcb->TrackMap[TrackNum].TrackFPOffset;//0;//NextChunkLenOth - FirstChunkLen;
    Vcb->TrackMap[TrackNum].LastLba = (Vcb->TrackMap[TrackNum].LastLba*Vcb->TrackMap[TrackNum].PacketSize) /
           (Vcb->TrackMap[TrackNum].PacketSize + 7);

    return STATUS_SUCCESS;
} // end UDFCheckTrackFPAddressing()

uint32
UDFFixFPAddress(
    IN PVCB           Vcb,               // Volume control block from this DevObj
    IN uint32         Lba
    )
{
    uint32 i = Vcb->LastReadTrack;    
    uint32 pk;
    uint32 rel;

//    if(Vcb->CompatFlags & UDF_VCB_IC_MRW_ADDR_PROBLEM) {
    if(Vcb->TrackMap[i].Flags & TrackMap_FixMRWAddressing) {
        pk = Lba / MRW_DA_SIZE;
        rel = Lba % MRW_DA_SIZE;
        Lba = pk*MRW_DMA_SEGMENT_SIZE + rel;
        Lba += MRW_DMA_OFFSET;
    }
    if(Vcb->TrackMap[i].Flags & TrackMap_FixFPAddressing) {
        if(Lba < 0x20)
            return Lba;
        pk = Lba / Vcb->TrackMap[i].PacketSize;
        rel = Lba % Vcb->TrackMap[i].PacketSize;
        UDFPrint(("FixFPAddr: %x -> %x\n", Lba, pk*(Vcb->TrackMap[i].PacketSize+7) + rel));
        return pk*(Vcb->TrackMap[i].PacketSize+7) + rel /*- Vcb->TrackMap[i].PacketFPOffset*/;
    }
    return Lba;
} // end UDFFixFPAddress()

#endif //_BROWSE_UDF_

/*
    detect device driver & try to read disk layout (use all methods)
 */
OSSTATUS
UDFGetDiskInfo(
    IN PDEVICE_OBJECT DeviceObject,      // the target device object
    IN PVCB           Vcb                // Volume control block from this DevObj
    )
{
    OSSTATUS        RC = STATUS_UNRECOGNIZED_VOLUME;
    int8*           ioBuf = (int8*)MyAllocatePool__(NonPagedPool,4096);
    uint8 MediaType;
    PLUN_WRITE_PERF_DESC_USER WPerfDesc;
    uint32 i;
//    BOOLEAN MRW_problem = FALSE;
    uint32 SavedFeatures = 0;
#ifdef UDF_FORMAT_MEDIA
    PUDFFmtState            fms = Vcb->fms;
#else
  #define fms FALSE
#endif //UDF_FORMAT_MEDIA

    UDFPrint(("UDFGetDiskInfo\n"));

    if(!ioBuf) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    _SEH2_TRY {
        RC = UDFGetBlockSize(DeviceObject, Vcb);
        if(!OS_SUCCESS(RC)) try_return(RC);
    
    
        // Get lower driver signature
        RC = UDFPhSendIOCTL(IOCTL_CDRW_GET_SIGNATURE,DeviceObject,
            ioBuf,sizeof(GET_SIGNATURE_USER_OUT),
            ioBuf,sizeof(GET_SIGNATURE_USER_OUT),
            TRUE,NULL);
    
        if(!OS_SUCCESS(RC)) {
    
            RC = UDFUseStandard(DeviceObject, Vcb);
#ifdef _BROWSE_UDF_
            if(!NT_SUCCESS(RC) || fms)
                try_return(RC);

            // assume Device Recordable for now
            goto GetSignatureFailed;
#endif //_BROWSE_UDF_
        }

        UDFPrint(("UDF: Signature of low driver is : %s \n",
            ((PGET_SIGNATURE_USER_OUT)(ioBuf))->VendorId));
    
        if(!strncmp( (const char *)(&( ((PGET_SIGNATURE_USER_OUT)(ioBuf))->VendorId[0]) ),
            Signature,strlen(Signature) )) {
            UDFPrint(("UDF: *****************************************\n"));
            UDFPrint(("UDF: ********* Our Device Driver Found ******\n"));
            UDFPrint(("UDF: *****************************************\n"));
    
            (Vcb->VCBFlags) |= UDF_VCB_FLAGS_OUR_DEVICE_DRIVER;
#ifndef _BROWSE_UDF_
            // reset driver
#ifdef UDF_FORMAT_MEDIA
            if(!fms->opt_probe) {
#endif //UDF_FORMAT_MEDIA
                UDFResetDeviceDriver(Vcb, Vcb->TargetDeviceObject, FALSE);
                // lock it
                ((PPREVENT_MEDIA_REMOVAL_USER_IN)(ioBuf))->PreventMediaRemoval = TRUE;
                UDFPhSendIOCTL( IOCTL_STORAGE_MEDIA_REMOVAL,
                                     DeviceObject,
                                     ioBuf,sizeof(PREVENT_MEDIA_REMOVAL_USER_IN),
                                     NULL,0,
                                     FALSE, NULL);
#ifdef UDF_FORMAT_MEDIA
            }
#endif //UDF_FORMAT_MEDIA
#endif //_BROWSE_UDF_
//#else //_BROWSE_UDF_
            // get device features
            UDFPhSendIOCTL( IOCTL_CDRW_GET_DEVICE_INFO,
                                 DeviceObject,
                                 NULL,0,
                                 ioBuf,sizeof(GET_DEVICE_INFO_USER_OUT),
                                 FALSE,NULL);

            Vcb->SavedFeatures =
                SavedFeatures = ((PGET_DEVICE_INFO_USER_OUT)ioBuf)->Features;
            if(!(SavedFeatures & CDRW_FEATURE_SYNC_ON_WRITE)) {
                UDFPrint(("UDFGetDiskInfo: UDF_VCB_IC_NO_SYNCCACHE_AFTER_WRITE\n"));
                Vcb->CompatFlags |= UDF_VCB_IC_NO_SYNCCACHE_AFTER_WRITE;
            }
            if(!(SavedFeatures & CDRW_FEATURE_FORCE_SYNC_BEFORE_READ)) {
                UDFPrint(("UDFGetDiskInfo: UDF_VCB_IC_SYNCCACHE_BEFORE_READ\n"));
                Vcb->CompatFlags |= UDF_VCB_IC_SYNCCACHE_BEFORE_READ;
            }
            if(SavedFeatures & CDRW_FEATURE_BAD_RW_SEEK) {
                UDFPrint(("UDFGetDiskInfo: CDRW_FEATURE_BAD_RW_SEEK\n"));
                Vcb->CompatFlags |= UDF_VCB_IC_BAD_RW_SEEK;
            }
            // we must check if this is FP-formatted disk in old devices
            // independently of MediaType they report
            if(SavedFeatures & CDRW_FEATURE_FP_ADDRESSING_PROBLEM) {
                UDFPrint(("UDFGetDiskInfo: CDRW_FEATURE_FP_ADDRESSING_PROBLEM ?\n"));
                Vcb->CompatFlags |= UDF_VCB_IC_FP_ADDR_PROBLEM;
            }
            if(SavedFeatures & CDRW_FEATURE_MRW_ADDRESSING_PROBLEM) {
                UDFPrint(("UDFGetDiskInfo: CDRW_FEATURE_MRW_ADDRESSING_PROBLEM ?\n"));
            }
            if(SavedFeatures & CDRW_FEATURE_FORCE_SYNC_ON_WRITE) {
                UDFPrint(("UDFGetDiskInfo: CDRW_FEATURE_FORCE_SYNC_ON_WRITE\n"));
                Vcb->VCBFlags |= UDF_VCB_FLAGS_FORCE_SYNC_CACHE;
            }
            if(SavedFeatures & CDRW_FEATURE_BAD_DVD_LAST_LBA) {
                UDFPrint(("UDFGetDiskInfo: CDRW_FEATURE_BAD_DVD_LAST_LBA\n"));
                Vcb->CompatFlags |= UDF_VCB_IC_BAD_DVD_LAST_LBA;
            }
            if(SavedFeatures & CDRW_FEATURE_STREAMING) {
                UDFPrint(("UDFGetDiskInfo: CDRW_FEATURE_STREAMING\n"));
            }
            if(SavedFeatures & CDRW_FEATURE_OPC) {
                UDFPrint(("UDFGetDiskInfo: CDRW_FEATURE_OPC -> assume OPCNum=1\n"));
                Vcb->OPCNum = 1;
            }
#ifdef UDF_FORMAT_MEDIA
            if(SavedFeatures & CDRW_FEATURE_FULL_BLANK_ON_FORMAT) {
                UDFPrint(("UDFGetDiskInfo: CDRW_FEATURE_FULL_BLANK_ON_FORMAT\n"));
                if((fms->opt_probe || fms->opt_smart_f)/* &&
                   (fms->format_media && fms->blank_media*/) {
                    UDFPrint(("UDFGetDiskInfo: force Full Erase\n"));
                    fms->opt_qblank = FALSE;
                }
            }
#endif //UDF_FORMAT_MEDIA
#ifdef _BROWSE_UDF_
            // get device buffer size
            RC = UDFPhSendIOCTL( IOCTL_CDRW_BUFFER_CAPACITY,
                                 DeviceObject,
                                 NULL,0,
                                 ioBuf,sizeof(BUFFER_CAPACITY_BLOCK_USER_OUT),
                                 FALSE,NULL);
            if(NT_SUCCESS(RC)) {
                Vcb->CdrwBufferSize = ((PBUFFER_CAPACITY_BLOCK_USER_OUT)ioBuf)->BufferLength;
            } else {
                Vcb->CdrwBufferSize = 0;
            }
            UDFPrint(("UDFGetDiskInfo: CdrwBufferSize = %dKb\n", Vcb->CdrwBufferSize / 1024));
            Vcb->CdrwBufferSizeCounter = 0;
#endif //_BROWSE_UDF_
            // get media type
            RC = UDFPhSendIOCTL(IOCTL_CDRW_GET_MEDIA_TYPE,DeviceObject,
                    NULL,0,ioBuf,sizeof(GET_MEDIA_TYPE_USER_OUT),
                    FALSE, NULL);
            if(!OS_SUCCESS(RC)) goto Try_FullToc;
            Vcb->MediaType =
            MediaType = ((PGET_MEDIA_TYPE_USER_OUT)ioBuf)->MediaType;
            UDFPrint(("UDFGetDiskInfo: MediaType %x\n", MediaType));

#ifndef UDF_FORMAT_MEDIA
            // we shall ignore audio-disks
            switch(MediaType) {
            case MediaType_120mm_CDROM_AudioOnly:
            case MediaType_80mm_CDROM_AudioOnly:
            case MediaType_120mm_CDR_AudioOnly:
            case MediaType_80mm_CDR_AudioOnly:
            case MediaType_120mm_CDRW_AudioOnly:
            case MediaType_80mm_CDRW_AudioOnly:
//            case :
                UDFPrint(("UDFGetDiskInfo: we shall ignore audio-disks...\n"));
                try_return(RC = STATUS_UNRECOGNIZED_VOLUME);
            }
#endif //UDF_FORMAT_MEDIA

            UDFPrint(("UDFGetDiskInfo: Check DVD-disks...\n"));
            RC = UDFPhSendIOCTL(IOCTL_CDRW_GET_MEDIA_TYPE_EX,DeviceObject,
                    NULL,0,ioBuf,sizeof(GET_MEDIA_TYPE_EX_USER_OUT),
                    FALSE, NULL);
            if(!OS_SUCCESS(RC)) goto Try_FullToc;
            Vcb->MediaClassEx =
            MediaType = (((PGET_MEDIA_TYPE_EX_USER_OUT)ioBuf)->MediaClass);
            UDFPrint(("UDFGetDiskInfo: MediaClassEx %x\n", MediaType));

#ifdef _BROWSE_UDF_
            if(!fms) {

                switch(MediaType) {

                case CdMediaClass_CDR:
                case CdMediaClass_DVDR:
                case CdMediaClass_DVDpR:
                case CdMediaClass_HD_DVDR:
                case CdMediaClass_BDR:
                    UDFPrint(("UDFGetDiskInfo: MediaClass R\n"));
                    Vcb->MediaType = MediaType_UnknownSize_CDR;
                    break;
                case CdMediaClass_CDRW:

                    if(SavedFeatures & CDRW_FEATURE_MRW_ADDRESSING_PROBLEM) {
                        UDFPrint(("UDFGetDiskInfo: CDRW_FEATURE_MRW_ADDRESSING_PROBLEM on CD-RW\n"));
                        Vcb->CompatFlags |= UDF_VCB_IC_MRW_ADDR_PROBLEM;
                    }

                case CdMediaClass_DVDRW:
                case CdMediaClass_DVDpRW:
                case CdMediaClass_DVDRAM:
                case CdMediaClass_HD_DVDRW:
                case CdMediaClass_HD_DVDRAM:
                case CdMediaClass_BDRE:
                    UDFPrint(("UDFGetDiskInfo: MediaClass RW\n"));
                    Vcb->MediaType = MediaType_UnknownSize_CDRW;
                    break;
                case CdMediaClass_CDROM:
                case CdMediaClass_DVDROM:
                case CdMediaClass_HD_DVDROM:
                case CdMediaClass_BDROM:
                    UDFPrint(("UDFGetDiskInfo: MediaClass ROM\n"));
                    Vcb->MediaType = MediaType_Unknown;
    //                    Vcb->MediaType = MediaType_UnknownSize_CDROM;
                    break;
                default:
                    UDFPrint(("UDFGetDiskInfo: MediaClass Unknown\n"));
                    Vcb->MediaType = MediaType_Unknown;
                    break;
                }
                MediaType = Vcb->MediaType;

            }
#endif //_BROWSE_UDF_

#ifdef UDF_FORMAT_MEDIA

            if(fms) {

                switch(MediaType) {
                case CdMediaClass_CDR:
                    UDFPrint(("CdMediaClass_CDR\n"));
                    MediaType = MediaType_UnknownSize_CDR;
                    if(fms->opt_media == MT_AUTO)
                        fms->opt_media = MT_CDR;
                    break;
                case CdMediaClass_DVDR:
                    UDFPrint(("CdMediaClass_DVDR -> MediaType_UnknownSize_CDR\n"));
                    MediaType = MediaType_UnknownSize_CDR;
                    if(fms->opt_media == MT_AUTO)
                        fms->opt_media = MT_DVDR;
                    break;
                case CdMediaClass_DVDpR:
                    UDFPrint(("CdMediaClass_DVDpR -> MediaType_UnknownSize_CDR\n"));
                    MediaType = MediaType_UnknownSize_CDR;
                    if(fms->opt_media == MT_AUTO)
                        fms->opt_media = MT_DVDpR;
                    break;
                case CdMediaClass_HD_DVDR:
                    UDFPrint(("CdMediaClass_HD_DVDR -> MediaType_UnknownSize_CDR\n"));
                    MediaType = MediaType_UnknownSize_CDR;
                    if(fms->opt_media == MT_AUTO)
                        fms->opt_media = MT_DVDR;
                    break;
                case CdMediaClass_BDR:
                    UDFPrint(("CdMediaClass_BDR -> MediaType_UnknownSize_CDR\n"));
                    MediaType = MediaType_UnknownSize_CDR;
                    if(fms->opt_media == MT_AUTO)
                        fms->opt_media = MT_DVDR;
                    break;
                case CdMediaClass_CDRW:
                    UDFPrint(("CdMediaClass_CDRW\n"));
                    MediaType = MediaType_UnknownSize_CDRW;
                    if(fms->opt_media == MT_AUTO)
                        fms->opt_media = MT_CDRW;
                    if(SavedFeatures & CDRW_FEATURE_MRW_ADDRESSING_PROBLEM) {
                        UDFPrint(("UDFGetDiskInfo: CDRW_FEATURE_MRW_ADDRESSING_PROBLEM on CD-RW\n"));
                        Vcb->CompatFlags |= UDF_VCB_IC_MRW_ADDR_PROBLEM;
                    }
                    break;
                case CdMediaClass_DVDRW:
                    UDFPrint(("  CdMediaClass_DVDRW -> MediaType_UnknownSize_CDRW\n"));
                    if(fms->opt_media == MT_AUTO)
                        fms->opt_media = MT_DVDRW;
                    MediaType = MediaType_UnknownSize_CDRW;
                    break;
                case CdMediaClass_DVDpRW:
                    UDFPrint(("  CdMediaClass_DVDpRW -> MediaType_UnknownSize_CDRW\n"));
                    if(fms->opt_media == MT_AUTO)
                        fms->opt_media = MT_DVDpRW;
                    MediaType = MediaType_UnknownSize_CDRW;
                    break;
                case CdMediaClass_DVDRAM:
                    UDFPrint(("  CdMediaClass_DVDRAM -> MediaType_UnknownSize_CDRW\n"));
                    if(fms->opt_media == MT_AUTO)
                        fms->opt_media = MT_DVDRAM;
                    MediaType = MediaType_UnknownSize_CDRW;
                    break;
                case CdMediaClass_HD_DVDRW:
                    UDFPrint(("  CdMediaClass_HD_DVDRW -> MediaType_UnknownSize_CDRW\n"));
                    if(fms->opt_media == MT_AUTO)
                        fms->opt_media = MT_DVDRW;
                    MediaType = MediaType_UnknownSize_CDRW;
                    break;
                case CdMediaClass_HD_DVDRAM:
                    UDFPrint(("  CdMediaClass_HD_DVDRAM -> MediaType_UnknownSize_CDRW\n"));
                    if(fms->opt_media == MT_AUTO)
                        fms->opt_media = MT_DVDRAM;
                    MediaType = MediaType_UnknownSize_CDRW;
                    break;
                case CdMediaClass_BDRE:
                    UDFPrint(("  CdMediaClass_BDRE -> MediaType_UnknownSize_CDRW\n"));
                    if(fms->opt_media == MT_AUTO)
                        fms->opt_media = MT_DVDRW;
                    MediaType = MediaType_UnknownSize_CDRW;
                    break;
                case CdMediaClass_NoDiscPresent:
                    UDFPrint(("  CdMediaClass_NoDiscPresent -> MediaType_NoDiscPresent\n"));
                    MediaType = MediaType_NoDiscPresent;
                    fms->opt_media = MT_none;
                    break;
                case CdMediaClass_DoorOpen:
                    UDFPrint(("  CdMediaClass_DoorOpen -> MediaType_DoorOpen\n"));
                    MediaType = MediaType_DoorOpen;
                    fms->opt_media = MT_none;
                    break;
                default:
                    UDFPrint(("  MediaType_Unknown\n"));
                    MediaType = MediaType_Unknown;
                    break;
                }
                if(!apply_force_r(fms)) {
                    my_exit(fms, MKUDF_CANT_APPLY_R);
                }
            }

#endif //UDF_FORMAT_MEDIA

            Vcb->DVD_Mode = (((PGET_MEDIA_TYPE_EX_USER_OUT)ioBuf)->MediaClassEx == CdMediaClassEx_DVD);
            Vcb->PhMediaCapFlags = ((PGET_MEDIA_TYPE_EX_USER_OUT)ioBuf)->CapFlags;
            Vcb->WriteParamsReq = (Vcb->PhMediaCapFlags & CdCapFlags_WriteParamsReq) ? TRUE : FALSE;
            if(Vcb->DVD_Mode &&
                !(Vcb->PhMediaCapFlags & CdCapFlags_RandomWritable)) {
                UDFPrint(("UDFGetDiskInfo: DVD && !CdCapFlags_RandomWritable\n"));
                UDFPrint(("  Read-only volume\n"));
//                BrutePoint();
#ifndef UDF_CDRW_EMULATION_ON_ROM
                Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY;
#endif
            }
#ifdef UDF_FORMAT_MEDIA
            if(fms) {
                if((MediaType == MediaType_NoDiscPresent) ||
                   (MediaType == MediaType_DoorOpen)) {
                    UserPrint(("No media in device\n"));
                    my_exit(fms, MKUDF_NO_MEDIA_IN_DEVICE);
                }
            }
#endif //UDF_FORMAT_MEDIA
            if(!Vcb->WriteParamsReq) {
                UDFPrint(("UDFGetDiskInfo: do not use WriteParams\n"));
            }
            if(Vcb->PhMediaCapFlags & CdCapFlags_Cav) {
                UDFPrint(("UDFGetDiskInfo: Use CAV (1)\n"));
                Vcb->VCBFlags |= UDF_VCB_FLAGS_USE_CAV;
            }

#ifdef _BROWSE_UDF_
            if(!fms) {
                // check if this device is capable to write on such media
                if(UDFIsDvdMedia(Vcb)) {
                    //RC =
                    UDFPrint(("UDFGetDiskInfo: update defaulted LastLBA\n"));
                    UDFGetBlockSize(DeviceObject,Vcb);
                    //if(!OS_SUCCESS(RC)) goto Try_FullToc;
                } else {
                    if((SavedFeatures & CDRW_FEATURE_MRW_ADDRESSING_PROBLEM) &&
                       (SavedFeatures & UDF_VCB_IC_FP_ADDR_PROBLEM)) {
                        UDFPrint(("UDFGetDiskInfo: CDRW_FEATURE_MRW_ADDRESSING_PROBLEM on old CD-ROM\n"));
                        Vcb->CompatFlags |= UDF_VCB_IC_MRW_ADDR_PROBLEM;
                    }
                }
            }
#endif //_BROWSE_UDF_

/*#ifdef UDF_FORMAT_MEDIA
            if(fms) {
                if(MediaType == CdMediaClass_DVDRW) {
                    UserPrint(("Not empty media. Erase required.\n"));
                    my_exit(fms, MKUDF_BLANK_FORMAT_REQUIRED);
                }
            }
#endif //UDF_FORMAT_MEDIA*/

#define cap ((PGET_CAPABILITIES_3_USER_OUT)ioBuf)
            // get device capabilities
            RC = UDFPhSendIOCTL(IOCTL_CDRW_GET_CAPABILITIES,DeviceObject,
                    NULL,0,ioBuf,sizeof(GET_CAPABILITIES_3_USER_OUT),
                    FALSE, NULL);
            if(!OS_SUCCESS(RC)) goto Try_FullToc;

            // check if this device is capable to write on such media
            RC = UDFPhSendIOCTL(IOCTL_DISK_IS_WRITABLE,DeviceObject,
                    NULL,0,NULL,0,FALSE, NULL);
            if(RC != STATUS_SUCCESS) {
                UDFPrint(("IS_WRITABLE - false, doing additional check...\n"));
                if( ((MediaType >= MediaType_UnknownSize_CDRW) && !(cap->WriteCap & DevCap_write_cd_rw)) ||
                    ((MediaType >= MediaType_UnknownSize_CDR) && !(cap->WriteCap & DevCap_write_cd_r)) ||
                     (MediaType < MediaType_UnknownSize_CDR) ) {
                    UserPrint(("Hardware Read-only volume\n"));
#ifndef UDF_CDRW_EMULATION_ON_ROM
                    Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY;
#endif  //UDF_CDRW_EMULATION_ON_ROM
#ifdef UDF_FORMAT_MEDIA
                    if(fms && !fms->opt_read_iso)
                        my_exit(fms, MKUDF_HW_READ_ONLY);
#endif //UDF_FORMAT_MEDIA
                }
            } else {
                UDFPrint(("Writable disk\n"));
            }
            Vcb->MaxWriteSpeed = cap->MaximumWriteSpeedSupported;
            Vcb->MaxReadSpeed  = cap->MaximumSpeedSupported;
            if(cap->PageLength >= (sizeof(GET_CAPABILITIES_3_USER_OUT)-2)) {
                Vcb->CurSpeed = max(cap->CurrentSpeed, cap->CurrentWriteSpeed3);
                if(cap->LunWPerfDescriptorCount && cap->LunWPerfDescriptorCount != 0xffff) {
                    ULONG n;
                    UDFPrint(("Write performance descriptor(s) found: %x\n", cap->LunWPerfDescriptorCount));
                    n = (4096 - sizeof(GET_CAPABILITIES_3_USER_OUT)) / sizeof(LUN_WRITE_PERF_DESC_USER);
                    n = min(n, cap->LunWPerfDescriptorCount);
                    // get device capabilities
                    RC = UDFPhSendIOCTL(IOCTL_CDRW_GET_CAPABILITIES,DeviceObject,
                            ioBuf,sizeof(GET_CAPABILITIES_3_USER_OUT)+n*sizeof(LUN_WRITE_PERF_DESC_USER),
                            ioBuf,sizeof(GET_CAPABILITIES_3_USER_OUT)+n*sizeof(LUN_WRITE_PERF_DESC_USER),
                            TRUE,NULL);
                    if(OS_SUCCESS(RC)) {
                        WPerfDesc = (PLUN_WRITE_PERF_DESC_USER)(ioBuf + sizeof(GET_CAPABILITIES_3_USER_OUT));
                        n = FALSE;
                        for(i = 0; i<n; i++) {
                            if((WPerfDesc[i].RotationControl & LunWPerf_RotCtrl_Mask) == LunWPerf_RotCtrl_CAV) {
                                Vcb->VCBFlags |= UDF_VCB_FLAGS_USE_CAV;
                                if(!n) {
                                    Vcb->CurSpeed = WPerfDesc[i].WriteSpeedSupported;
                                    n = TRUE;
                                    UDFPrint(("Use CAV\n"));
                                } else {
                                    Vcb->CurSpeed = max(WPerfDesc[i].WriteSpeedSupported, Vcb->CurSpeed);
                                }
                                UDFPrint(("supports speed %dX\n", Vcb->CurSpeed/176));
                                //break;
                            }
                        }
                        if(n) {
                            UDFPrint(("Set r/w speeds to %dX\n", Vcb->CurSpeed/176));
                            Vcb->MaxWriteSpeed =
                            Vcb->MaxReadSpeed  = Vcb->CurSpeed;
                        }
                    }
                }
            } else {
                Vcb->CurSpeed = max(cap->CurrentSpeed, cap->CurrentWriteSpeed);
            }
            UDFPrint((" Speeds r/w %dX/%dX\n", Vcb->CurSpeed/176, cap->CurrentWriteSpeed/176));

            if(Vcb->VCBFlags & UDF_VCB_FLAGS_USE_CAV) {
                // limit both read & write speed to last write speed for CAV mode
                // some drives damage data when speed is adjusted during recording process
                // even in packet mode
                UDFSetSpeeds(Vcb);
            }
            UDFSetCaching(Vcb);

#undef cap
#ifdef UDF_FORMAT_MEDIA
            if(fms) {
                if( (fms->auto_media || (fms->opt_media == MT_AUTO)) &&
                       (fms->opt_media < MT_DVDR) ) {
                    if(MediaType < MediaType_UnknownSize_CDRW) {
                        fms->opt_media = MT_CDR;
                    } else {
                        fms->opt_media = MT_CDRW;
                    }
                }
                if(!apply_force_r(fms)) {
                    my_exit(fms, MKUDF_CANT_APPLY_R);
                }
            }
#endif //UDF_FORMAT_MEDIA
            RC = UDFReadDiscTrackInfo(DeviceObject, Vcb);

            if(!OS_SUCCESS(RC)) {
                // may be we have a CD-ROM device
Try_FullToc:
                UDFPrint(("Hardware Read-only volume (2)\n"));
//                BrutePoint();
#ifndef UDF_CDRW_EMULATION_ON_ROM
                Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY;
#endif

                RC = UDFReadAndProcessFullToc(DeviceObject, Vcb);
    
                if(!OS_SUCCESS(RC)) {
                    RC = UDFUseStandard(DeviceObject,Vcb);
                    if(!OS_SUCCESS(RC)) try_return(RC);
                }
    
            }
        } else {
#ifdef _BROWSE_UDF_
GetSignatureFailed:
#endif
            RC = UDFUseStandard(DeviceObject, Vcb);
            if(!OS_SUCCESS(RC)) try_return(RC);
        }
    
try_exit:   NOTHING;

    } _SEH2_FINALLY {

        if(ioBuf) MyFreePool__(ioBuf);

        if(UDFIsDvdMedia(Vcb) &&
           (Vcb->CompatFlags & UDF_VCB_IC_BAD_DVD_LAST_LBA) &&
           (Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) &&
            Vcb->LastLBA &&
           (Vcb->LastLBA < DEFAULT_LAST_LBA_DVD)) {
            UDFPrint(("UDF: Bad DVD last LBA %x, fixup!\n", Vcb->LastLBA));
            Vcb->LastLBA = DEFAULT_LAST_LBA_DVD;
            Vcb->NWA = 0;
        }


        if(UDFIsDvdMedia(Vcb) && !Vcb->FirstLBA && !Vcb->LastPossibleLBA) {
            UDFPrint(("UDF: Empty DVD. Use bogus values for now\n"));
            Vcb->LastPossibleLBA = DEFAULT_LAST_LBA_DVD;
            Vcb->LastLBA = 0;
        }
        
        if((Vcb->LastPossibleLBA & 0x80000000) || (Vcb->LastPossibleLBA < Vcb->LastLBA)) {
            UDFPrint(("UDF: bad LastPossibleLBA %x -> %x\n", Vcb->LastPossibleLBA, Vcb->LastLBA));
            Vcb->LastPossibleLBA = Vcb->LastLBA;
        }
        if(!Vcb->WriteBlockSize)
            Vcb->WriteBlockSize = PACKETSIZE_UDF*Vcb->BlockSize;

#ifdef _BROWSE_UDF_
        if(Vcb->TrackMap) {
            if(Vcb->TrackMap[Vcb->LastTrackNum].LastLba > Vcb->NWA) {
                if(Vcb->NWA) {
                    if(Vcb->TrackMap[Vcb->LastTrackNum].DataParam & TrkInfo_FP) {
                        Vcb->LastLBA = Vcb->NWA-1;
                    } else {
                        Vcb->LastLBA = Vcb->NWA-7-1;
                    }
                }
            } else {
                if((Vcb->LastTrackNum > 1) &&
                   (Vcb->TrackMap[Vcb->LastTrackNum-1].FirstLba >= Vcb->TrackMap[Vcb->LastTrackNum-1].LastLba)) {
                    Vcb->LastLBA = Vcb->TrackMap[Vcb->LastTrackNum-1].LastLba;
                }
            }
        }

        for(i=0; i<32; i++) {
            if(!(Vcb->LastPossibleLBA >> i))
                break;
        }
        if(i > 20) {
            Vcb->WCacheBlocksPerFrameSh = max(Vcb->WCacheBlocksPerFrameSh, (2*i)/5+2);
            Vcb->WCacheBlocksPerFrameSh = min(Vcb->WCacheBlocksPerFrameSh, 16);
        }

        if(Vcb->CompatFlags & UDF_VCB_IC_FP_ADDR_PROBLEM) {
            // Check first 0x200 blocks
            UDFCheckTrackFPAddressing(Vcb, Vcb->FirstTrackNum);
            // if we really have such a problem, fix LastLBA
            if(Vcb->CompatFlags & UDF_VCB_IC_FP_ADDR_PROBLEM) {
                UDFPrint(("UDF: Fix LastLBA: %x -> %x\n", Vcb->LastLBA, (Vcb->LastLBA*32) / 39));
                Vcb->LastLBA = (Vcb->LastLBA*32) / 39;
            }
        }
#endif //_BROWSE_UDF_

        if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) {
            if(!Vcb->BlankCD && Vcb->MediaType != MediaType_UnknownSize_CDRW) {
                UDFPrint(("UDFGetDiskInfo: R/O+!Blank+!RW -> !RAW\n"));
                Vcb->VCBFlags &= ~UDF_VCB_FLAGS_RAW_DISK;
            } else {
                UDFPrint(("UDFGetDiskInfo: Blank or RW\n"));
            }
        }

        UDFPrint(("UDF: ------------------------------------------\n"));
        UDFPrint(("UDF: Media characteristics\n"));
        UDFPrint(("UDF: Last session: %d\n",Vcb->LastSession));
        UDFPrint(("UDF: First track in first session: %d\n",Vcb->FirstTrackNum));
        UDFPrint(("UDF: First track in last session: %d\n",Vcb->FirstTrackNumLastSes));
        UDFPrint(("UDF: Last track in last session: %d\n",Vcb->LastTrackNum));
        UDFPrint(("UDF: First LBA in first session: %x\n",Vcb->FirstLBA));
        UDFPrint(("UDF: First LBA in last session: %x\n",Vcb->FirstLBALastSes));
        UDFPrint(("UDF: Last LBA in last session: %x\n",Vcb->LastLBA));
        UDFPrint(("UDF: First writable LBA (NWA) in last session: %x\n",Vcb->NWA));
        UDFPrint(("UDF: Last available LBA beyond end of last session: %x\n",Vcb->LastPossibleLBA));
        UDFPrint(("UDF: blocks per frame: %x\n",1 << Vcb->WCacheBlocksPerFrameSh));
        UDFPrint(("UDF: Flags: %s%s\n",
                 Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK ? "RAW " : "",
                 Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY ? "R/O " : "WR "
                 ));
        UDFPrint(("UDF: ------------------------------------------\n"));

#ifdef UDF_FORMAT_MEDIA
        if(fms && fms->opt_disk_info) {
            UserPrint(("------------------------------------------\n"));
            UserPrint(("Media characteristics:\n"));
            UserPrint(("  First writable LBA (NWA) in last session: %x\n",Vcb->NWA));
            UserPrint(("  Last available LBA beyond end of last session: %x\n",Vcb->LastPossibleLBA));
            UserPrint(("------------------------------------------\n"));
        }
#endif //UDF_FORMAT_MEDIA

    } _SEH2_END;

    UDFPrint(("UDFGetDiskInfo: %x\n", RC));
    return(RC);

} // end UDFGetDiskInfo()

//#ifdef _BROWSE_UDF_

OSSTATUS
UDFPrepareForReadOperation(
    IN PVCB Vcb,
    IN uint32 Lba,
    IN uint32 BCount
    )
{
    if( (Vcb->FsDeviceType != FILE_DEVICE_CD_ROM_FILE_SYSTEM) ) {
        Vcb->VCBFlags &= ~UDF_VCB_LAST_WRITE;
        return STATUS_SUCCESS;
    }
    uint32 i = Vcb->LastReadTrack;
    BOOLEAN speed_changed = FALSE;
#ifdef _BROWSE_UDF_
    PUCHAR tmp;
    OSSTATUS RC;
    SIZE_T ReadBytes;
#endif //_BROWSE_UDF_

#ifdef _UDF_STRUCTURES_H_
    if(Vcb->BSBM_Bitmap) {
        ULONG i;
        for(i=0; i<BCount; i++) {
            if(UDFGetBit((uint32*)(Vcb->BSBM_Bitmap), Lba+i)) {
                UDFPrint(("R: Known BB @ %#x\n", Lba));
                //return STATUS_FT_WRITE_RECOVERY; // this shall not be treated as error and
                                                   // we shall get IO request to BAD block
                return STATUS_DEVICE_DATA_ERROR;
            }
        }
    }
#endif //_UDF_STRUCTURES_H_

    if(!UDFIsDvdMedia(Vcb) &&
        (Vcb->VCBFlags & UDF_VCB_LAST_WRITE) &&
       !(Vcb->VCBFlags & UDF_VCB_FLAGS_NO_SYNC_CACHE) &&
       !(Vcb->CompatFlags & UDF_VCB_IC_NO_SYNCCACHE_AFTER_WRITE)){

//        OSSTATUS RC;

        RC = UDFSyncCache(Vcb);
    }
    if( (Vcb->VCBFlags & UDF_VCB_LAST_WRITE) &&
       !(Vcb->VCBFlags & UDF_VCB_FLAGS_NO_SYNC_CACHE) &&
#ifndef UDF_FORMAT_MEDIA
        (Vcb->CompatFlags & UDF_VCB_IC_SYNCCACHE_BEFORE_READ) &&
#endif //UDF_FORMAT_MEDIA
        TRUE)
    {
//        OSSTATUS RC;
        UDFSyncCache(Vcb);
    }

#ifdef _BROWSE_UDF_
    if(!UDFIsDvdMedia(Vcb)) {
        // limit read speed after write operation
        // to avoid performance degrade durring speed-up/down
        // on read/write mode switching
        if(Vcb->VCBFlags & UDF_VCB_FLAGS_USE_CAV) {
            // limit both read & write speed to last write speed for CAV mode
            // some drives damage data when speed is adjusted during recording process
            // even in packet mode
            if(Vcb->CurSpeed != Vcb->MaxWriteSpeed ||
               Vcb->CurSpeed != Vcb->MaxReadSpeed) {
                Vcb->CurSpeed = Vcb->MaxWriteSpeed;
                speed_changed = TRUE;
            }
        } else
        if(Vcb->VCBFlags & UDF_VCB_LAST_WRITE) {
            // limit read speed to last write speed
            if(Vcb->CurSpeed > Vcb->MaxWriteSpeed) {
                Vcb->CurSpeed = Vcb->MaxWriteSpeed;
                speed_changed = TRUE;
            }
        } else
        if(Vcb->CurSpeed < Vcb->MaxReadSpeed ) {
            // increment read speed (+1X)
            Vcb->CurSpeed += 176/1;
            speed_changed = TRUE;
        }

        if(Vcb->CurSpeed > Vcb->MaxReadSpeed) {
            Vcb->CurSpeed = Vcb->MaxReadSpeed;
        }
        // send speed limits to drive
        if(speed_changed) {
            RtlZeroMemory(&(Vcb->SpeedBuf), sizeof(SET_CD_SPEED_EX_USER_IN));
            Vcb->SpeedBuf.ReadSpeed  = Vcb->CurSpeed;
            Vcb->SpeedBuf.WriteSpeed = Vcb->MaxWriteSpeed;
            if(Vcb->VCBFlags & UDF_VCB_FLAGS_USE_CAV) {
                Vcb->SpeedBuf.RotCtrl = CdSpeed_RotCtrl_CAV;
            }
            UDFPrint(("    UDFPrepareForReadOperation: set speed to %s %dX/%dX\n",
                (Vcb->VCBFlags & UDF_VCB_FLAGS_USE_CAV) ? "CAV" : "CLV",
                Vcb->SpeedBuf.ReadSpeed,
                Vcb->SpeedBuf.WriteSpeed));
            UDFPhSendIOCTL(IOCTL_CDRW_SET_SPEED,
                                Vcb->TargetDeviceObject,
                                &(Vcb->SpeedBuf),sizeof(SET_CD_SPEED_EX_USER_IN),
                                NULL,0,TRUE,NULL);
        }
    }

    if(UDFIsDvdMedia(Vcb))
        return STATUS_SUCCESS;

    if(Vcb->LastReadTrack &&
       ((Vcb->TrackMap[i].FirstLba <= Lba) || (Vcb->TrackMap[i].FirstLba & 0x80000000)) &&
       (Vcb->TrackMap[i].LastLba >= Lba)) {
check_for_data_track:
        // check track mode (Mode1/XA)
        switch((Vcb->TrackMap[i].DataParam & TrkInfo_Dat_Mask)) {
        case TrkInfo_Dat_Mode1: // Mode1
        case TrkInfo_Dat_XA:    // XA Mode2
        case TrkInfo_Dat_Unknown: // for some stupid irons
            UDFSetMRWMode(Vcb);
            break;
        default:
            Vcb->IncrementalSeekState = INCREMENTAL_SEEK_NONE;
            return STATUS_INVALID_PARAMETER;
        }
    } else {
        for(i=Vcb->FirstTrackNum; i<=Vcb->LastTrackNum; i++) {
            if(((Vcb->TrackMap[i].FirstLba > Lba) && !(Vcb->TrackMap[i].FirstLba & 0x80000000)) ||
               (Vcb->TrackMap[i].LastLba < Lba))
                continue;
            Vcb->LastReadTrack = i;
            goto check_for_data_track;
        }
        Vcb->LastReadTrack = 0;
    }
    if(Vcb->IncrementalSeekState != INCREMENTAL_SEEK_WORKAROUND) {
        Vcb->IncrementalSeekState = INCREMENTAL_SEEK_NONE;
        return STATUS_SUCCESS;
    }
    UDFPrint(("    UDFPrepareForReadOperation: seek workaround...\n"));
    Vcb->IncrementalSeekState = INCREMENTAL_SEEK_DONE;

    tmp = (PUCHAR)DbgAllocatePoolWithTag(NonPagedPool, Vcb->BlockSize, 'bNWD');
    if(!tmp) {
        Vcb->IncrementalSeekState = INCREMENTAL_SEEK_NONE;
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    for(i=0x1000; i<=Lba; i+=0x1000) {
        RC = UDFPhReadSynchronous(Vcb->TargetDeviceObject, tmp, Vcb->BlockSize,
                   ((uint64)UDFFixFPAddress(Vcb,i)) << Vcb->BlockSizeBits, &ReadBytes, 0);
        UDFPrint(("    seek workaround, LBA %x, status %x\n", i, RC));
    }
    DbgFreePool(tmp);
#endif //_BROWSE_UDF_

    return STATUS_SUCCESS;
} // end UDFPrepareForReadOperation()

//#endif //_BROWSE_UDF_

void
UDFUpdateNWA(
    PVCB Vcb,
    uint32 LBA,  // physical
    uint32 BCount,
    OSSTATUS RC
    )
{
#ifndef UDF_READ_ONLY_BUILD
#ifdef _BROWSE_UDF_
    if(!OS_SUCCESS(RC)) {
        return;
    }
    if(!Vcb->CDR_Mode) {

        if((Vcb->MediaClassEx == CdMediaClass_DVDRW ||
            Vcb->MediaClassEx == CdMediaClass_DVDpRW ||
            Vcb->MediaClassEx == CdMediaClass_DVDRAM ||
            Vcb->MRWStatus == DiscInfo_BGF_Interrupted ||
            Vcb->MRWStatus == DiscInfo_BGF_InProgress)
              && (LBA+BCount-1) > Vcb->LastLBA) {
            ASSERT(Vcb->NWA > Vcb->LastLBA);
            Vcb->NWA = LBA+BCount;
            Vcb->LastLBA = Vcb->NWA-1;
        }
        if(Vcb->VCBFlags & UDF_VCB_FLAGS_FORCE_SYNC_CACHE)
            goto sync_cache;
/*        if(Vcb->CdrwBufferSize) {
            Vcb->CdrwBufferSizeCounter += BCount * 2048;
            if(Vcb->CdrwBufferSizeCounter >= Vcb->CdrwBufferSize + 2*2048) {
                UDFPrint(("    UDFUpdateNWA: buffer is full, sync...\n"));
                Vcb->CdrwBufferSizeCounter = 0;
                goto sync_cache;
            }
        }*/
        if(Vcb->SyncCacheState == SYNC_CACHE_RECOVERY_RETRY) {
            Vcb->VCBFlags |= UDF_VCB_FLAGS_FORCE_SYNC_CACHE;
        }
        Vcb->SyncCacheState = SYNC_CACHE_RECOVERY_NONE;
        return;
    }
    if(Vcb->LastLBA < (LBA+BCount))
        Vcb->LastLBA = LBA+BCount;
    if(Vcb->NWA)
        Vcb->NWA+=BCount+7;
sync_cache:
    if(!(Vcb->CompatFlags & UDF_VCB_IC_NO_SYNCCACHE_AFTER_WRITE)) {
        UDFPrint(("    UDFUpdateNWA: syncing...\n"));
        RC = UDFSyncCache(Vcb);
    }
#endif //_BROWSE_UDF_
#endif //UDF_READ_ONLY_BUILD
} // end UDFUpdateNWA()


/*
    This routine reads physical sectors
 */
/*OSSTATUS
UDFReadSectors(
    IN PVCB Vcb,
    IN BOOLEAN Translate,       // Translate Logical to Physical
    IN uint32 Lba,
    IN uint32 BCount,
    OUT int8* Buffer,
    OUT PSIZE_T ReadBytes
    )
{

    if(Vcb->FastCache.ReadProc && (KeGetCurrentIrql() < DISPATCH_LEVEL)) {
        return WCacheReadBlocks__(&(Vcb->FastCache), Vcb, Buffer, Lba, BCount, ReadBytes);
    }
    return UDFTRead(Vcb, Buffer, BCount*Vcb->BlockSize, Lba, ReadBytes);
} // end UDFReadSectors()*/

#ifdef _BROWSE_UDF_

/*
    This routine reads physical sectors
 */
OSSTATUS
UDFReadInSector(
    IN PVCB Vcb,
    IN BOOLEAN Translate,       // Translate Logical to Physical
    IN uint32 Lba,
    IN uint32 i,                 // offset in sector
    IN uint32 l,                 // transfer length
    IN BOOLEAN Direct,          // Disable access to non-cached data
    OUT int8* Buffer,
    OUT PSIZE_T ReadBytes
    )
{
    int8* tmp_buff;
    OSSTATUS status;
    SIZE_T _ReadBytes;

    (*ReadBytes) = 0;
    if(WCacheIsInitialized__(&(Vcb->FastCache)) && (KeGetCurrentIrql() < DISPATCH_LEVEL)) {
        status = WCacheDirect__(&(Vcb->FastCache), Vcb, Lba, FALSE, &tmp_buff, Direct);
        if(OS_SUCCESS(status)) {
            (*ReadBytes) += l;
            RtlCopyMemory(Buffer, tmp_buff+i, l);
        }
        if(!Direct) WCacheEODirect__(&(Vcb->FastCache), Vcb);
    } else {
        if(Direct) {
            return STATUS_INVALID_PARAMETER;
        }
        tmp_buff = (int8*)MyAllocatePool__(NonPagedPool, Vcb->BlockSize);
        if(!tmp_buff) return STATUS_INSUFFICIENT_RESOURCES;
        status = UDFReadSectors(Vcb, Translate, Lba, 1, FALSE, tmp_buff, &_ReadBytes);
        if(OS_SUCCESS(status)) {
            (*ReadBytes) += l;
            RtlCopyMemory(Buffer, tmp_buff+i, l);
        }
        MyFreePool__(tmp_buff);
    }
    return status;
} // end UDFReadInSector()

/*
    This routine reads data of unaligned offset & length
 */
OSSTATUS
UDFReadData(
    IN PVCB Vcb,
    IN BOOLEAN Translate,       // Translate Logical to Physical
    IN int64 Offset,
    IN uint32 Length,
    IN BOOLEAN Direct,          // Disable access to non-cached data
    OUT int8* Buffer,
    OUT PSIZE_T ReadBytes
    )
{
    uint32 i, l, Lba, BS=Vcb->BlockSize;
    uint32 BSh=Vcb->BlockSizeBits;
    OSSTATUS status;
    SIZE_T _ReadBytes = 0;
    Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
    uint32 to_read;

    (*ReadBytes) = 0;
    if(!Length) return STATUS_SUCCESS;
    if(Vcb->VCBFlags & UDF_VCB_FLAGS_DEAD)
        return STATUS_NO_SUCH_DEVICE;
    // read tail of the 1st sector if Offset is not sector_size-aligned
    Lba = (uint32)(Offset >> BSh);
    if((i = (uint32)(Offset & (BS-1)))) {
        l = (BS - i) < Length ?
            (BS - i) : Length;
        // here we use 'ReadBytes' 'cause now it's set to zero
        status = UDFReadInSector(Vcb, Translate, Lba, i, l, Direct, Buffer, ReadBytes);
        if(!OS_SUCCESS(status)) return status;
        if(!(Length = Length - l)) return STATUS_SUCCESS;
        Lba ++;
        Buffer += l;
    }
    // read sector_size-aligned part
    i = Length >> BSh;
    while(i) {
        to_read = min(i, 64);
        status = UDFReadSectors(Vcb, Translate, Lba, to_read, Direct, Buffer, &_ReadBytes);
        (*ReadBytes) += _ReadBytes;
        if(!OS_SUCCESS(status)) {
            return status;
        }
        Buffer += to_read<<BSh;
        Length -= to_read<<BSh;
        Lba += to_read;
        i -= to_read;
    }
    // read head of the last sector
    if(!Length) return STATUS_SUCCESS;
    status = UDFReadInSector(Vcb, Translate, Lba, 0, Length, Direct, Buffer, &_ReadBytes);
    (*ReadBytes) += _ReadBytes;

    return status;
} // end UDFReadData()

#endif //_BROWSE_UDF_

#ifndef UDF_READ_ONLY_BUILD
/*
    This routine writes physical sectors. This routine supposes Lba & Length
    alignment on WriteBlock (packet) size.
 */
OSSTATUS
UDFWriteSectors(
    IN PVCB Vcb,
    IN BOOLEAN Translate,       // Translate Logical to Physical
    IN uint32 Lba,
    IN uint32 BCount,
    IN BOOLEAN Direct,          // Disable access to non-cached data
    IN int8* Buffer,
    OUT PSIZE_T WrittenBytes
    )
{
    OSSTATUS status;

#ifdef _BROWSE_UDF_
    if(!Vcb->Modified || (Vcb->IntegrityType == INTEGRITY_TYPE_CLOSE)) {
        UDFSetModified(Vcb);
        if(Vcb->LVid && !Direct) {
            status = UDFUpdateLogicalVolInt(Vcb,FALSE);
        }
    }

    if(Vcb->CDR_Mode) {
        if(Vcb->LastLBA < Lba+BCount-1)
            Vcb->LastLBA = Lba+BCount-1;
    }
#endif //_BROWSE_UDF_

    if(Vcb->FastCache.WriteProc && (KeGetCurrentIrql() < DISPATCH_LEVEL)) {
        status = WCacheWriteBlocks__(&(Vcb->FastCache), Vcb, Buffer, Lba, BCount, WrittenBytes, Direct);
        ASSERT(OS_SUCCESS(status));
#ifdef _BROWSE_UDF_
        UDFClrZeroBits(Vcb->ZSBM_Bitmap, Lba, BCount);
#endif //_BROWSE_UDF_
        return status;
    }
/*    void* buffer;
    OSSTATUS status;
    SIZE_T _ReadBytes;
    (*WrittenBytes) = 0;
    buffer = DbgAllocatePool(NonPagedPool, Vcb->WriteBlockSize);
    if(!buffer) return STATUS_INSUFFICIENT_RESOURCES;
    status = UDFTRead(Vcb, Buffer, BCount<<Vcb->BlockSizeBits, (Lba&~(Vcb->WriteBlockSize-1), _WrittenBytes);*/
#ifdef UDF_DBG
    status = UDFTWrite(Vcb, Buffer, BCount<<Vcb->BlockSizeBits, Lba, WrittenBytes);
    ASSERT(OS_SUCCESS(status));
    return status;
#else // UDF_DBG
    return UDFTWrite(Vcb, Buffer, BCount<<Vcb->BlockSizeBits, Lba, WrittenBytes);
#endif // UDF_DBG
} // end UDFWriteSectors()

OSSTATUS
UDFWriteInSector(
    IN PVCB Vcb,
    IN BOOLEAN Translate,       // Translate Logical to Physical
    IN uint32 Lba,
    IN uint32 i,                 // offset in sector
    IN uint32 l,                 // transfer length
    IN BOOLEAN Direct,          // Disable access to non-cached data
    OUT int8* Buffer,
    OUT PSIZE_T WrittenBytes
    )
{
    int8* tmp_buff;
    OSSTATUS status;
#ifdef _BROWSE_UDF_
    SIZE_T _WrittenBytes;
    SIZE_T ReadBytes;

    if(!Vcb->Modified) {
        UDFSetModified(Vcb);
        if(Vcb->LVid)
            status = UDFUpdateLogicalVolInt(Vcb,FALSE);
    }

    if(Vcb->CDR_Mode) {
        if(Vcb->LastLBA < Lba)
            Vcb->LastLBA = Lba;
    }
#endif //_BROWSE_UDF_

    (*WrittenBytes) = 0;
#ifdef _BROWSE_UDF_
    if(WCacheIsInitialized__(&(Vcb->FastCache)) && (KeGetCurrentIrql() < DISPATCH_LEVEL)) {
#endif //_BROWSE_UDF_
        status = WCacheDirect__(&(Vcb->FastCache), Vcb, Lba, TRUE, &tmp_buff, Direct);
        if(OS_SUCCESS(status)) {
#ifdef _BROWSE_UDF_
            UDFClrZeroBit(Vcb->ZSBM_Bitmap, Lba);
#endif //_BROWSE_UDF_
            (*WrittenBytes) += l;
            RtlCopyMemory(tmp_buff+i, Buffer, l);
        }
        if(!Direct) WCacheEODirect__(&(Vcb->FastCache), Vcb);
#ifdef _BROWSE_UDF_
    } else {
        // If Direct = TRUE we should never get here, but...
        if(Direct) {
            BrutePoint();
            return STATUS_INVALID_PARAMETER;
        }
        tmp_buff = (int8*)MyAllocatePool__(NonPagedPool, Vcb->BlockSize);
        if(!tmp_buff) {
            BrutePoint();
            return STATUS_INSUFFICIENT_RESOURCES;
        }
        // read packet
        status = UDFReadSectors(Vcb, Translate, Lba, 1, FALSE, tmp_buff, &ReadBytes);
        if(!OS_SUCCESS(status)) goto EO_WrSctD;
        // modify packet
        RtlCopyMemory(tmp_buff+i, Buffer, l);
        // write modified packet
        status = UDFWriteSectors(Vcb, Translate, Lba, 1, FALSE, tmp_buff, &_WrittenBytes);
        if(OS_SUCCESS(status))
            (*WrittenBytes) += l;
EO_WrSctD:
        MyFreePool__(tmp_buff);
    }
    ASSERT(OS_SUCCESS(status));
    if(!OS_SUCCESS(status)) {
        UDFPrint(("UDFWriteInSector() for LBA %x failed\n", Lba));
    }
#endif //_BROWSE_UDF_
    return status;
} // end UDFWriteInSector()

/*
    This routine writes data at unaligned offset & length
 */
OSSTATUS
UDFWriteData(
    IN PVCB Vcb,
    IN BOOLEAN Translate,      // Translate Logical to Physical
    IN int64 Offset,
    IN SIZE_T Length,
    IN BOOLEAN Direct,         // setting this flag delays flushing of given
                               // data to indefinite term
    IN int8* Buffer,
    OUT PSIZE_T WrittenBytes
    )
{
    uint32 i, l, Lba, BS=Vcb->BlockSize;
    uint32 BSh=Vcb->BlockSizeBits;
    OSSTATUS status;
    SIZE_T _WrittenBytes;
    Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;

    (*WrittenBytes) = 0;
    if(!Length) return STATUS_SUCCESS;
    if(Vcb->VCBFlags & UDF_VCB_FLAGS_DEAD)
        return STATUS_NO_SUCH_DEVICE;
    // write tail of the 1st sector if Offset is not sector_size-aligned
    Lba = (uint32)(Offset >> BSh);
    if((i = ((uint32)Offset & (BS-1)))) {
        l = (BS - i) < Length ?
            (BS - i) : Length;
        status = UDFWriteInSector(Vcb, Translate, Lba, i, l, Direct, Buffer, WrittenBytes);
        if(!OS_SUCCESS(status)) return status;
        if(!(Length = Length - l)) return STATUS_SUCCESS;
        Lba ++;
        Buffer += l;
    }
    // write sector_size-aligned part
    i = Length >> BSh;
    if(i) {
        status = UDFWriteSectors(Vcb, Translate, Lba, i, Direct, Buffer, &_WrittenBytes);
        (*WrittenBytes) += _WrittenBytes;
        if(!OS_SUCCESS(status)) return status;
        l = i<<BSh;
#ifdef _BROWSE_UDF_
        UDFClrZeroBits(Vcb->ZSBM_Bitmap, Lba, i);
#endif //_BROWSE_UDF_
        if(!(Length = Length - l)) return STATUS_SUCCESS;
        Lba += i;
        Buffer += l;
    }
    status = UDFWriteInSector(Vcb, Translate, Lba, 0, Length, Direct, Buffer, &_WrittenBytes);
    (*WrittenBytes) += _WrittenBytes;
#ifdef _BROWSE_UDF_
    UDFClrZeroBit(Vcb->ZSBM_Bitmap, Lba);
#endif //_BROWSE_UDF_

    return status;
} // end UDFWriteData()

#endif //UDF_READ_ONLY_BUILD

OSSTATUS
UDFResetDeviceDriver(
    IN PVCB Vcb,
    IN PDEVICE_OBJECT TargetDeviceObject,
    IN BOOLEAN Unlock
    )
{
    PCDRW_RESET_DRIVER_USER_IN tmp = (PCDRW_RESET_DRIVER_USER_IN)
        MyAllocatePool__(NonPagedPool, sizeof(CDRW_RESET_DRIVER_USER_IN));
    OSSTATUS RC;
    if(!tmp)
        return STATUS_INSUFFICIENT_RESOURCES;
    RtlZeroMemory(tmp, sizeof(CDRW_RESET_DRIVER_USER_IN));
    tmp->UnlockTray = (Unlock ? 1 : 0);
    tmp->MagicWord = 0x3a6 | (Unlock ? 1 : 0);
    RC = UDFPhSendIOCTL(IOCTL_CDRW_RESET_DRIVER_EX, TargetDeviceObject,
        tmp, sizeof(CDRW_RESET_DRIVER_USER_IN), NULL, 0, TRUE,NULL);
    if(Vcb) {
        Vcb->LastReadTrack = 0;
        Vcb->LastModifiedTrack = 0;
        Vcb->OPCDone = FALSE;
        if((Vcb->VCBFlags & UDF_VCB_FLAGS_USE_CAV) &&
           Vcb->TargetDeviceObject) {
            // limit both read & write speed to last write speed for CAV mode
            // some drives damage data when speed is adjusted during recording process
            // even in packet mode
            UDFSetSpeeds(Vcb);
        }
        UDFSetCaching(Vcb);
    }

    MyFreePool__(tmp);
    return RC;
} // end UDFResetDeviceDriver()

OSSTATUS
UDFSetSpeeds(
    IN PVCB Vcb
    )
{
    OSSTATUS RC;

    RtlZeroMemory(&(Vcb->SpeedBuf), sizeof(SET_CD_SPEED_EX_USER_IN));
    if(Vcb->VCBFlags & UDF_VCB_FLAGS_USE_CAV) {
        Vcb->SpeedBuf.RotCtrl = CdSpeed_RotCtrl_CAV;
        Vcb->CurSpeed =
        Vcb->SpeedBuf.ReadSpeed =
        Vcb->SpeedBuf.WriteSpeed = Vcb->MaxWriteSpeed;
    } else {
        Vcb->SpeedBuf.ReadSpeed  = Vcb->CurSpeed;
        Vcb->SpeedBuf.WriteSpeed = Vcb->MaxWriteSpeed;
    }
    UDFPrint(("    UDFSetSpeeds: set speed to %s %dX/%dX\n",
        (Vcb->VCBFlags & UDF_VCB_FLAGS_USE_CAV) ? "CAV" : "CLV",
        Vcb->SpeedBuf.ReadSpeed / 176,
        Vcb->SpeedBuf.WriteSpeed / 176));
    RC = UDFPhSendIOCTL(IOCTL_CDRW_SET_SPEED,
                        Vcb->TargetDeviceObject,
                        &(Vcb->SpeedBuf),sizeof(SET_CD_SPEED_EX_USER_IN),
                        NULL,0,TRUE,NULL);
    UDFPrint(("UDFSetSpeeds: %x\n", RC));
    return RC;
} // end UDFSetSpeeds()

NTSTATUS
UDFSetCaching(
    IN PVCB Vcb
    )
{
#pragma pack(push,1)
    struct {
        MODE_PARAMETER_HEADER    Header;
        MODE_CACHING_PAGE        Data;
        CHAR Padding [16];
    } CachingPage;

    struct {
        MODE_PARAMETER_HEADER         Header;
        MODE_READ_WRITE_RECOVERY_PAGE Data;
        CHAR Padding [16];
    } RecoveryPage;
#ifdef _MSC_VER
#pragma pack(pop,1)
#else
#pragma pack(pop)
#endif

    MODE_SENSE_USER_IN ModeSenseCtl;
    OSSTATUS RC;

    UDFPrint(("UDFSetCaching:\n"));

    ModeSenseCtl.PageCode.Byte = MODE_PAGE_ERROR_RECOVERY;
    RC = UDFPhSendIOCTL(IOCTL_CDRW_MODE_SENSE, Vcb->TargetDeviceObject,
                    &ModeSenseCtl,sizeof(ModeSenseCtl),
                    (PVOID)&RecoveryPage,sizeof(RecoveryPage),
                    FALSE, NULL);
    if(OS_SUCCESS(RC)) {
        UDFPrint(("  Error recovery page:\n"
            "PageCode         %d\n"
            "PageLength       %d\n"

            "DCRBit %d\n"
            "DTEBit %d\n"
            "PERBit %d\n"
            "EERBit %d\n"
            "RCBit  %d\n"
            "TBBit  %d\n"
            "ARRE   %d\n"
            "AWRE   %d\n"

            "ReadRetryCount %d\n"
            "CorrectionSpan %d\n"
            "HeadOffsetCount %d\n"
            "DataStrobOffsetCount %d\n"

            "ErrorRecoveryParam2.Fields.EMCDR %d\n"

            "WriteRetryCount %d\n",

            RecoveryPage.Data.PageCode,
            RecoveryPage.Data.PageLength,

            RecoveryPage.Data.ErrorRecoveryParam.Fields.DCRBit,
            RecoveryPage.Data.ErrorRecoveryParam.Fields.DTEBit,
            RecoveryPage.Data.ErrorRecoveryParam.Fields.PERBit,
            RecoveryPage.Data.ErrorRecoveryParam.Fields.EERBit,
            RecoveryPage.Data.ErrorRecoveryParam.Fields.RCBit,
            RecoveryPage.Data.ErrorRecoveryParam.Fields.TBBit,
            RecoveryPage.Data.ErrorRecoveryParam.Fields.ARRE,
            RecoveryPage.Data.ErrorRecoveryParam.Fields.AWRE,

            RecoveryPage.Data.ReadRetryCount,
            RecoveryPage.Data.CorrectionSpan,
            RecoveryPage.Data.HeadOffsetCount,
            RecoveryPage.Data.DataStrobOffsetCount,

            RecoveryPage.Data.ErrorRecoveryParam2.Fields.EMCDR,

            RecoveryPage.Data.WriteRetryCount

        ));
    }

    ModeSenseCtl.PageCode.Byte = MODE_PAGE_CACHING;
    RC = UDFPhSendIOCTL(IOCTL_CDRW_MODE_SENSE, Vcb->TargetDeviceObject,
                    &ModeSenseCtl,sizeof(ModeSenseCtl),
                    (PVOID)&CachingPage,sizeof(CachingPage),
                    FALSE, NULL);
    if(!OS_SUCCESS(RC)) {
        return RC;
    }

    UDFPrint(("  Caching page:\n"
        "PageCode         %d\n"
        "PageLength       %d\n"
        "ReadDisableCache %d\n"
        "MultiplicationFactor %d\n"
        "WriteCacheEnable %d\n"
        "WriteRetensionPriority %d\n"
        "ReadRetensionPriority  %d\n",

        CachingPage.Data.PageCode,
        CachingPage.Data.PageLength,
        CachingPage.Data.ReadDisableCache,
        CachingPage.Data.MultiplicationFactor,
        CachingPage.Data.WriteCacheEnable,
        CachingPage.Data.WriteRetensionPriority,
        CachingPage.Data.ReadRetensionPriority
    ));

    RtlZeroMemory(&CachingPage.Header, sizeof(CachingPage.Header));
    CachingPage.Data.PageCode = MODE_PAGE_CACHING;
    CachingPage.Data.PageSavable = 0;
    if( CachingPage.Data.ReadDisableCache ||
       !CachingPage.Data.WriteCacheEnable) {
        CachingPage.Data.ReadDisableCache = 0;
        CachingPage.Data.WriteCacheEnable = 1;
        RC = UDFPhSendIOCTL(IOCTL_CDRW_MODE_SELECT, Vcb->TargetDeviceObject,
                        (PVOID)&CachingPage,sizeof(CachingPage.Header) + 2 + CachingPage.Data.PageLength,
                        NULL,0,
                        FALSE, NULL);
    } else {
        RC = STATUS_SUCCESS;
    }
    UDFPrint(("UDFSetCaching: %x\n", RC));
    return RC;
} // end UDFSetCaching()