#include "pch.h"
#include "udfskd.h"
#include "fatkd.h"

#include <ntddcdrm.h>
#include <ntdddisk.h>
#include <ntddscsi.h>

#include "..\..\udfs\nodetype.h"

#ifdef UDFS_RW_IN_BUILD
#include "..\..\udfsrw\nodetype.h"
#endif

#include "..\..\udfs\udf.h"
#include "..\..\udfs\udfstruc.h"
#include "..\..\udfs\udfdata.h"

//
//  13346/UDF on disc note type codes and dump routines
//


DUMP_ROUTINE( DumpUdfOnDiscIcbFile);
DUMP_ROUTINE( DumpUdfOnDiscFid);
DUMP_ROUTINE( DumpUdfOnDiscFsd);
DUMP_ROUTINE( DumpUdfOnDiscLvol);
DUMP_ROUTINE( DumpUdfOnDiscPartDesc);
DUMP_ROUTINE( DumpUdfOnDiscVdp);
DUMP_ROUTINE( DumpUdfOnDiscAnchor);
DUMP_ROUTINE( DumpUdfOnDiscPvd);
DUMP_ROUTINE( DumpUdfOnDiscIcbExtFile);


static NODE_TYPE_INFO_NEW UdfOnDiscTypeCodes[] = {
    {   DESTAG_ID_NSR_PVD,          "(3/10.1) PVD",             NULL, DumpUdfOnDiscPvd },
    {   DESTAG_ID_NSR_ANCHOR,       "(3/10.2) AVDP",            NULL, DumpUdfOnDiscAnchor },
    {   DESTAG_ID_NSR_VDP,          "(3/10.3) VDP",             NULL, DumpUdfOnDiscVdp },
    {   DESTAG_ID_NSR_IMPUSE,       "(3/10.4) IUVD",            NULL, NULL },
    {   DESTAG_ID_NSR_PART,         "(3/10.5) PD",              NULL, DumpUdfOnDiscPartDesc },
    {   DESTAG_ID_NSR_LVOL,         "(3/10.6) LVD",             NULL, DumpUdfOnDiscLvol },
    {   DESTAG_ID_NSR_UASD,         "(3/10.8) UASD",            NULL, NULL },
    {   DESTAG_ID_NSR_TERM,         "(3/10.9) TD",              NULL, NULL },
    {   DESTAG_ID_NSR_LVINTEG,      "(3/10.10) LVID",           NULL, NULL },

    {   DESTAG_ID_NSR_FSD,          "(4/14.1) FSD",             NULL, DumpUdfOnDiscFsd },
    {   DESTAG_ID_NSR_FID,          "(4/14.4) FID",             NULL, DumpUdfOnDiscFid },
    {   DESTAG_ID_NSR_ALLOC,        "(4/14.5) AED",             NULL, NULL },
    {   DESTAG_ID_NSR_ICBIND,       "(4/14.7) ICB Indirect",    NULL, NULL },
    {   DESTAG_ID_NSR_ICBTRM,       "(4/14.8) ICB Terminal",    NULL, NULL },
    {   DESTAG_ID_NSR_FILE,         "(4/14.9) FE",              NULL, DumpUdfOnDiscIcbFile },
    {   DESTAG_ID_NSR_EXT_FILE,     "(4/14.17) EXT FE",         NULL, DumpUdfOnDiscIcbExtFile },
    {   DESTAG_ID_NSR_EA,           "(4/14.10) EAH",            NULL, NULL },
    {   DESTAG_ID_NSR_UASE,         "(4/14.11) USE",            NULL, NULL },
    {   DESTAG_ID_NSR_SBP,          "(4/14.12) SBD",            NULL, NULL },
    {   DESTAG_ID_NSR_PINTEG,       "(4/14.13) PI",             NULL, NULL },
    
    {   0, "UNKNOWN/NotSpec", NULL }
};

static PCHAR IcbStrategies[] = {
    "NOTSPEC",  
    "4/A.2 TREE",  
    "4/A.3 MASTER",  
    "4/A.4 BALTREE",  
    "4/A.5 DIRECT"
    };

#define MAX_VALID_ICB_STRAT 4

static PCHAR IcbFileTypes[] = {
    "NOTSPEC",
    "UASPACE",
    "PINTEG",
    "INDIRECT",
    "DIRECTORY",
    "FILE",
    "BLOCKDEV",
    "CHARDEV",
    "XA",
    "FIFO",
    "SOCKET",
    "TERMINAL",
    "PATHLINK",
    "STREAMDIR"
    };

#define MAX_VALID_ICB_TYPE 13

static STATE UdfIcbTagFlags[] = {

    {   ICBTAG_F_ALLOC_MASK,        ICBTAG_F_ALLOC_SHORT,       "ShortAlloc"},
    {   ICBTAG_F_ALLOC_MASK,        ICBTAG_F_ALLOC_LONG,        "LongAlloc"},
    {   ICBTAG_F_ALLOC_MASK,        ICBTAG_F_ALLOC_EXTENDED,    "ExtAlloc"},
    {   ICBTAG_F_ALLOC_MASK,        ICBTAG_F_ALLOC_IMMEDIATE,   "ImmAlloc"},

    {   ICBTAG_F_SORTED,            ICBTAG_F_SORTED,            "Sorted(4/8.6.1)"},
    {   ICBTAG_F_NO_RELOCATE,       ICBTAG_F_NO_RELOCATE,       "Nonrelocatable"},
    {   ICBTAG_F_ARCHIVE,           ICBTAG_F_ARCHIVE,           "Archive"},
    {   ICBTAG_F_SETUID,            ICBTAG_F_SETUID,            "SetUid"},
    {   ICBTAG_F_SETGID,            ICBTAG_F_SETGID,            "SetGid"},
    {   ICBTAG_F_STICKY,            ICBTAG_F_STICKY,            "Sticky"},
    {   ICBTAG_F_CONTIGUOUS,        ICBTAG_F_CONTIGUOUS,        "Contiguous"},
    {   ICBTAG_F_SYSTEM,            ICBTAG_F_SYSTEM,            "System"},
    {   ICBTAG_F_TRANSFORMED,       ICBTAG_F_TRANSFORMED,       "Transformed"},
    {   ICBTAG_F_MULTIVERSIONS,     ICBTAG_F_MULTIVERSIONS,     "MultiVersions"},
    {   0}
};


static STATE UdfFidFlags[] = {

    {   NSR_FID_F_HIDDEN,           NSR_FID_F_HIDDEN,       "Hidden"},
    {   NSR_FID_F_DIRECTORY,        NSR_FID_F_DIRECTORY,    "Directory"},
    {   NSR_FID_F_DELETED,          NSR_FID_F_DELETED,      "Deleted"},
    {   NSR_FID_F_PARENT,           NSR_FID_F_PARENT,       "Parent"},
    {   NSR_FID_F_META,             NSR_FID_F_META,         "MetadataStream"},
    { 0 }
};


//  LCB summary dump used by F/Scb tree dump

VOID
UdfSummaryLcbDumpRoutine(
    IN ULONG64 RemoteAddress,
    IN LONG Options
    )
{
    USHORT Type;
    ULONG64 Scb;
    ULONG Offset;

    ReadM( &Type, RemoteAddress, sizeof( Type));

#ifdef UDFS_RW_IN_BUILD
    if (( UDFS_NTC_LCB != Type) && (UDFSRW_NTC_LCB != Type)) {
#else
    if ( UDFS_NTC_LCB != Type) {
#endif
        dprintf( "Not a UDF LCB @ %08x\n", RemoteAddress);
        return;
    }
    
    if (Options & 2)  {
    
        DumpUdfLcb( RemoteAddress, (Options & 8) ? 1 : 0, &NewNodeTypeCodes[ TypeCodeInfoIndex( Type)]);
    }
    else  {

        GetFieldValue( RemoteAddress, 
                       "udfs!_LCB", 
                       (UDFS_NTC_LCB != Type) ? "ChildScb" : "ChildFcb", 
                       Scb);

        dprintf( "\nUDF LCB @ %I64x   Child F/Scb: %I64x  ",  RemoteAddress, Scb);

        GetFieldOffset( "udfs!_LCB", "FileName", &Offset);
        DumpStr( Offset, RemoteAddress + Offset, "FileName", FALSE, TRUE);
    }
}


// OK
DUMP_ROUTINE( DumpUdfFcb)

/*++

Routine Description:

    Dump a specific fcb.

Arguments:

    Address - Gives the address of the fcb to dump

Return Value:

    None

--*/

{
    ULONG Result;
    UINT64 NonP;
    ULONG Flags, FcbState;
    ULONG Offset, Offsetb;

    ROE( GetFieldValue( Address, InfoNode->TypeName, "FcbState", FcbState));

    //
    //  For R/O udfs,  dump the FCB flags and common header flags.  RW FCB
    //  is less interesting,  nothing to dump.
    //
    
    if (!NTC_IS_UDFS_RW( InfoNode->TypeCode))  {
    
        dprintf("[ Option flags:  1 = list children,  2 = List parent links,  4 = dump Mcb ]\n\n");

        dprintf("FcbState: ");
        PrintState( UdfFcbState, FcbState );

        ROE( GetFieldValue( Address, InfoNode->TypeName, "Flags", Flags));
        dprintf("\nHeader.Flags: ");
        PrintState( HeaderFlags, Flags );

        ROE( GetFieldValue( Address, InfoNode->TypeName, "Flags2", Flags));    // TODO:
        dprintf("\nHeader.Flags2: ");
        PrintState( HeaderFlags2, Flags );
        dprintf("\n");
    }
    
    //
    //  Having established that this looks like an fcb, let's dump the
    //  interesting parts.
    //
    

    Dt( InfoNode->TypeName, Address, 0, 0, NULL);

    // TODO: Does the above dump the union data/index part?

    dprintf("\n");
    
    //
    //  Nonpaged portion
    //
    
    ROE( GetFieldValue( Address, InfoNode->TypeName, "FcbNonpaged", NonP));

    if (NonP)  {
    
        dprintf("\nNonpaged portion @ %I64x\n\n",NonP);

        Dt( "Udfs!FCB_NONPAGED", NonP, 0, 0, NULL);
    }
    
    //
    //  R/O UDFS:  Dump all Fcb children 
    //

    if (!NTC_IS_UDFS_RW( InfoNode->TypeCode))  {
    
        if (( Options & 1)  && (UDFS_NTC_FCB_INDEX == InfoNode->TypeCode)) {

            dprintf("\nChild Lcb list\n");

            ROE( GetFieldOffset( "udfs!_FCB", "ChildLcbQueue", &Offset));
            ROE( GetFieldOffset( "udfs!_LCB", "ParentFcbLinks", &Offsetb));
            
            DumpList( Address + Offset,
                      UdfSummaryLcbDumpRoutine,
                      Offsetb,
                      FALSE,
                      0 );
        }

        if (Options & 2)  {
        
            dprintf("\nParent Lcb list\n");
            
            ROE( GetFieldOffset( "udfs!_FCB", "ParentLcbQueue", &Offset));
            ROE( GetFieldOffset( "udfs!_LCB", "ChildFcbLinks", &Offsetb));
            
            DumpList( Address + Offset,
                      UdfSummaryLcbDumpRoutine,
                      Offsetb,
                      FALSE,
                      0 );
        }
        
        if (Options & 4)  {
        
            if (FcbState & FCB_STATE_MCB_INITIALIZED)  {

                ROE( GetFieldOffset( "udfs!_FCB", "Mcb", &Offset));
                dprintf("\nMcb\n");
                DumpLargeMcb( Address+Offset, 0, NULL);
            }
        }
    }
    else {

        //
        // TODO: RW UDFS - dump attached stream Scb list?
        //
    }
    
    dprintf( "\n" );
}


// OK
DUMP_ROUTINE( DumpUdfLcb)
{
    ULONG Flags;
    ULONG Ntc;

    ROE( GetFieldValue(  Address, InfoNode->TypeName, "NodeTypeCode", Ntc));
    ROE( GetFieldValue(  Address, InfoNode->TypeName, "Flags", Flags));
    
    dprintf("Lcb.Flags: ");
    PrintState( NTC_IS_UDFS_RW(Ntc) ? UdfRwLcbFlags : UdfLcbFlags, Flags );
    dprintf( "\n");

    Dt( InfoNode->TypeName, Address, Options, 0, NULL); // TODO: Expand parent list?

    if (Options >= 1)  {

        ULONG64 Scb;

        dprintf("\n\nChild S/Fcb:\n");

        ROE( GetFieldValue( Scb, 
                            InfoNode->TypeName, 
                            (Ntc == UDFS_NTC_LCB) ? "ChildFcb" : "ChildScb",
                            Scb));
                            
        DumpAnyStruct( Scb, 0, NULL);
    }
}


// OK
DUMP_ROUTINE( DumpUdfPcb)
{
    ULONG Result;
    ULONG Offset;
    ULONG Current = 0;
    ULONG Partitions;
    ULONG Flags;
    ULONG Size;

    ROE( GetFieldValue(  Address, InfoNode->TypeName, "Flags", Flags));

    dprintf( "\nPcb.Flags: ");
    PrintState( UdfPcbFlags, Flags);

    Dt( InfoNode->TypeName, Address, Options, 0, NULL);

    ROE( GetFieldValue(  Address, InfoNode->TypeName, "Partitions", Partitions));

    if (Partitions > 5)  {

        dprintf("Bogus partition count %d\n", Partitions);
        return;
    }

    ROE( GetFieldOffset( InfoNode->TypeName, "Partition", &Offset));
    Address += Offset;

    Size = GetTypeSize( "Udfs!PARTITION");

    for (Current = 0;  Current < Partitions; ++Current)  {

        dprintf("\nRef: %d\n", Current);
        
        Dt( "Udfs!PARTITION", Address, Options, 0, NULL);

        Address += Size;
    }
}

// OK
DUMP_ROUTINE( DumpUdfCcb)
{
    ULONG Flags;
    ULONG Ntc;

    ROE( GetFieldValue(  Address, InfoNode->TypeName, "NodeTypeCode", Ntc));
    ROE( GetFieldValue(  Address, InfoNode->TypeName, "Flags", Flags));
    
    dprintf( "Ccb.Flags: ");
    PrintState( NTC_IS_UDFS_RW(Ntc) ? UdfRwCcbFlags : UdfCcbFlags, Flags);
    dprintf( "\n");

    Dt( InfoNode->TypeName, Address, Options, 0, NULL);
}

// OK
DUMP_ROUTINE( DumpUdfIrpContextLite)
{

    Dt( InfoNode->TypeName, Address, Options, 0, NULL);

    // *** FIXME - dump delayed close queue
}

// OK
DUMP_ROUTINE( DumpUdfIrpContext)
{
    ULONG Flags;
    ULONG Ntc;

    ROE( GetFieldValue(  Address, InfoNode->TypeName, "NodeTypeCode", Ntc));
    ROE( GetFieldValue(  Address, InfoNode->TypeName, "Flags", Flags));
    
    dprintf("\nIrpContext Flags: ");
    PrintState( NTC_IS_UDFS_RW(Ntc) ? UdfRwIrpContextFlags : UdfIrpContextFlags, Flags );
    dprintf( "\n");

    Dt( InfoNode->TypeName, Address, Options, 0, NULL);
}


// OK
DUMP_ROUTINE( DumpUdfIcbSearchContext)
{
    Dt( "udfs!_ICB_SEARCH_CONTEXT", Address, 1, 0, NULL);   // TODO: expand current/active only
}


// OK
DUMP_ROUTINE( DumpLargeMcb)
{
    ULONG PairCount = 0, EntryCount = 0, Result;
    ULONG64 Mapping;
    PMAPPING Mappings, Current;
    ULONG PrevVbn = 0;
    
    dprintf("\nLARGE MCB @ %I64X\n", Address);    

    Dt( "udfs!LARGE_MCB", Address, 0,0, NULL);
    dprintf("\n");
    
    ROE( GetFieldValue( Address,  "udfs!LARGE_MCB", "PairCount", PairCount));
    ROE( GetFieldValue( Address,  "udfs!LARGE_MCB", "Mapping", Mapping));
    
    Mappings = calloc( PairCount, sizeof( MAPPING));

    if (NULL == Mappings)  {

        dprintf("Failed to allocate space for %d mappings\n", PairCount);
        return;
    }

    if (!ReadMemory( Mapping,  Mappings, sizeof( MAPPING)*PairCount, &Result ))  {
    
        dprintf("Unable to read mapping info @ %08x", Mapping);
        return;
    }

    Current = Mappings;
    
    for ( Current = Mappings;  
          EntryCount < PairCount;  
          ++EntryCount, ++Current)  {

        dprintf("(%08x,%08x) -> %08x\n", PrevVbn, Current->NextVbn - PrevVbn, Current->Lbn);    
        PrevVbn = Current->NextVbn;
    }

    dprintf("\n");
    free( Mappings);
}


// OK
DUMP_ROUTINE( DumpUdfVcb)
{    
    ULONG Flags;
    ULONG Ntc;
    FIELD_INFO Alloc[] = { //{ ".", NULL, 0,  0, 0, NULL},
                           { "AllocSup.", NULL, 0,  DBG_DUMP_FIELD_RECUR_ON_THIS,0, NULL}
                         };

    ROE( GetFieldValue(  Address, InfoNode->TypeName, "NodeTypeCode", Ntc));
    ROE( GetFieldValue(  Address, InfoNode->TypeName, "VcbState", Flags));
    
    dprintf("VcbState: ");
    PrintState( NTC_IS_UDFS_RW(Ntc) ? UdfRwVcbStateFlags : UdfVcbStateFlags, Flags );
    dprintf("\n");

    Dt( InfoNode->TypeName, Address, Options, 0, NULL);
    dprintf("\n");

    //
    //  For RW UDFS,  expand some more fields.
    //
    
    if (NTC_IS_UDFS_RW( Ntc))  {

        Dt( InfoNode->TypeName, Address, Options, 1, Alloc);
    }
}


DUMP_ROUTINE( DumpUdfVdo)
{
    USHORT Ntc;
    PUSHORT pNtc;
    ULONG Offset;
    ULONG Result;

    RM( Address, Ntc, pNtc, PUSHORT, Result );
    
#ifndef UDFS_RW_IN_BUILD
    if (UDFS_NTC_VCB == Ntc)  {
#else
    if ((UDFS_NTC_VCB == Ntc) || (UDFSRW_NTC_VCB == Ntc))  {
#endif
        //
        //  Looks like we've been given a VCB pointer.  Work back to the containing vdo.
        //

        dprintf("Backtracking to containing VDO from VCB...");

        ROE( GetFieldOffset( "udfs!VOLUME_DEVICE_OBJECT", "Vcb", &Offset));

        Address -= Offset;
    }
    
    dprintf( "\nUDFS Volume device object @ %08lx\n",  Address );

    Dt( "Udfs!VOLUME_DEVICE_OBJECT", Address, Options, 0, NULL);    
}


VOID
DumpTreeLcb(
    IN ULONG64 Lcb,
    IN LONG RecurLevel
    );

static UCHAR RecurDumpBuf[512];
static ULONG __i;

#define DoIndent(I)  for ( __i = 0;  __i < (I);  ++__i) RecurDumpBuf[__i] = ' ';  \
                   RecurDumpBuf[ __i] = 0;                                       \
                   dprintf( RecurDumpBuf);

static WCHAR RDumpNameBuf[512];


//
//  Called from outside for "main" stream SCBs,
//  recursive for stream directories / stream Scbs
//

// OK
static VOID
DumpTreeRecur (
    IN ULONG64 Scb,
    IN ULONG RecurLevel
    )
{
    ULONG64 Fcb, SdScb, Lcb;
    ULONG Ur, Cc, R, Offset, Offsetb, Error, Flags = 0;
    USHORT Type, Indent, Lc;
    UNICODE_STRING Name;
    PUCHAR TypeString = NULL;
    BOOLEAN Stream, Index, StreamDir, Data;

    if ((*ExtensionApis.lpCheckControlCRoutine)())  {
    
        dprintf("CTRL+C - aborting\n");
        
        // Raise out - could be looping in many levels...

        *((PULONG)(0)) = 0;
        return;    
    }

    if (RecurLevel > 100)  {

        dprintf("*** Recursed more than 100 deep - aborting ***\n");
        return;
    }
    
    //
    //  dump S/Fcb vitals
    //

    Indent = ((USHORT)RecurLevel) << 1;

    if (0 == Scb)  {

        DoIndent( Indent);
        dprintf("Called with NULL F/Scb\n");
        return;
    }

    ReadM( &Type, Scb, sizeof( Type));

    if (!NTC_IS_UDFS_RW( Type))  {

        Stream = StreamDir = FALSE;
        TypeString = "Udfs!FCB";
        Fcb = 0;

        Index = Type == UDFS_NTC_FCB_INDEX;
        Data  = Type == UDFS_NTC_FCB_DATA;
    }
    else {

#ifdef UDFS_RW_IN_BUILD

        TypeString = "Udfs!SCB";

        ROE( GetFieldValue( Scb, "udfs!_SCB", "ScbState", Flags));

        Stream = 0 != (Flags & 0x10);  // scb_state_secondary_stream
        StreamDir = 0 != (Flags & 0x08); // stream_directory

        if ((Error = GetFieldValue( Scb, "udfs!_SCB", "Fcb", Fcb)) || (0 == Fcb))  {

            DoIndent( Indent);
            
            if (Error)  {
            
                dprintf("Error reading Fcb from Scb %d\n", Error);
            }
            else {
            
                dprintf("<NULL Fcb ptr>\n");
            }            
        }
        
        Index = NodeIsUdfsRwIndex( Type);
        Data  = NodeIsUdfsRwData( Type);
#else
        dprintf("This FSKD build does not support R/W UDFS structures\n");
        return;
#endif
    }
    
    //
    //  If this is a new file,  as opposed to a stream of a file we've already dumped,
    //  them dump out the FCB and follow the stream directory.  For R/O UDFS there's
    //  nothing to do here (No streams & F/Scb split)
    //

#ifdef UDFS_RW_IN_BUILD
    
    if (!Stream && NTC_IS_UDFS_RW( Type))  {

        DoIndent( Indent);
        dprintf("FCB     @ 0x%I64x\n", Fcb);
    
        if ((0 != Fcb) && !(Error = GetFieldValue( Fcb, "udfs!_FCB", "StreamDirectoryScb", SdScb)) &&
            (0 != SdScb))  {

            //
            // TODO: Count SCBs in chain,  verify match scbs under streamdir?
            //
            
            ROE( GetFieldValue( SdScb, "udfs!_SCB", "NodeTypeCode", Type));

            if (NodeIsUdfsRwIndex(Type))  {

                //
                //  Stream dir - dump details than trawl through LCBs
                //

                ROE( GetFieldValue( SdScb, "udfs!_SCB", "ScbUserReference", Ur)); // TODO: Don't bail here!
                ROE( GetFieldValue( SdScb, "udfs!_SCB", "ScbReference", R));
                ROE( GetFieldValue( SdScb, "udfs!_SCB", "ScbCleanup", Cc));

                DoIndent( Indent);
                dprintf("SCB     @ 0x%I64X (%X,%X,%X) (-STREAMDIR-) \n", SdScb, Cc, Ur, R);

                //
                //  Follow all Lcbs and recursively dump tree
                //

                ROE( GetFieldOffset( "udfs!_SCB", "ChildLcbQueue", &Offset));
                ROE( GetFieldOffset( "udfs!_LCB", "ParentScbLinks", &Offsetb));

                DumpList( SdScb + Offset,
                          DumpTreeLcb,
                          Offsetb,
                          FALSE,
                          RecurLevel + 1);
            }
            else {
            
                DoIndent( Indent);
                dprintf("Invalid StreamDir Scb nodetypecode %l\n", Type);
            }
        }
        else {

            DoIndent( Indent);
            
            if (Error)  {
            
                dprintf("Error reading streamdirscb from fcb %d\n", Error);
            }
            else {
            
                dprintf("<NULL stream dir or Fcb>\n");
            }
        }
    }
#endif
    
    if (!StreamDir)  {

        // old (win2k) udfs didn't have this field.  Ignore failure.
        
        Lc = 0xffff;
        GetFieldValue( Scb, TypeString, "LinkCount", Lc);

        if (NTC_IS_UDFS_RW( Type))  {
        
#ifdef UDFS_RW_IN_BUILD        
            ROE( GetFieldValue( Scb, TypeString, "ScbUserReference", Ur));
            ROE( GetFieldValue( Scb, TypeString, "ScbReference", R));
            ROE( GetFieldValue( Scb, TypeString, "ScbCleanup", Cc));
#endif
        }
        else {
            ROE( GetFieldValue( Scb, TypeString, "FcbUserReference", Ur));
            ROE( GetFieldValue( Scb, TypeString, "FcbReference", R));
            ROE( GetFieldValue( Scb, TypeString, "FcbCleanup", Cc));
        }
        
        if (Index)  {

            //
            //  Index - dump details then trawl Lcbs
            //

            DoIndent( Indent);
            dprintf("F/SCB (-DIR-) @ 0x%I64X (%d,%d,%d - %d)\n", Scb, Cc, Ur, R, Lc);

            if (NTC_IS_UDFS_RW( Type) && (0 != Fcb))  {

                if (!GetFieldValue( Fcb, "udfs!_FCB", "DefaultStreamScb", SdScb))  {

                    if (SdScb != Scb)  {

                        DoIndent(Indent);
                        dprintf("INCONSISTENT - Scb->Fcb->DefaultStreamScb != Scb\n");
                    }
                }
            }

            if (Stream)  {
            
                DoIndent( Indent);
                dprintf("ILLEGAL - secondary stream is index\n");
            }

            //
            //  Follow all Lcbs and recursively dump tree
            //

            ROE( GetFieldOffset( TypeString, "ChildLcbQueue", &Offset));
            ROE( GetFieldOffset( "udfs!_LCB", 
                                 NTC_IS_UDFS_RW( Type) ? "ParentScbLinks" : "ParentFcbLinks",
                                 &Offsetb));

            DumpList( Scb + Offset,
                      DumpTreeLcb,
                      Offsetb,
                      FALSE,
                      RecurLevel + 1);                      
        }
        else if (Data)  {

            //
            //  File - dump details
            //
            
            DoIndent( Indent);
            dprintf("F/SCB (-DATA-) @ 0x%I64X (%d,%d,%d - %d)\n", Scb, Cc, Ur, R, Lc);
        }
        else {
        
            DoIndent( Indent);
            dprintf("Invalid F/Scb nodetypecode %d\n", Type);
        }
    }
}

//
//  Dump an LCB and all below it.
//

// OK
VOID
DumpTreeLcb(
    IN ULONG64 Lcb,
    IN LONG RecurLevel
    )
{
    ULONG64 Scb;
    ULONG Offset;
    ULONG Ntc;
    ULONG Ref;

    ROE( GetFieldValue( Lcb, "udfs!_LCB", "NodeTypeCode", Ntc));

#ifdef UDFS_RW_IN_BUILD
    if (( UDFS_NTC_LCB != Ntc) && (UDFSRW_NTC_LCB != Ntc))  {
#else
    if ( UDFS_NTC_LCB != Ntc)  {
#endif
        dprintf( "Not a UDF LCB @ %I64x - list corrupt\n", Lcb);
        return;
    }

    GetFieldValue( Lcb, 
                   "udfs!_LCB", 
                   NTC_IS_UDFS_RW( Ntc) ? "ChildScb" : "ChildFcb",
                   Scb);

    ROE( GetFieldValue( Lcb, "udfs!_LCB", "Reference", Ref));

    DoIndent( ((ULONG)RecurLevel) << 1);
    dprintf( "LCB @ %I64x  (Ref: %d)  Child F/Scb: %I64x ",  Lcb, Ref, Scb);

#ifdef UDFS_RW_IN_BUILD
    if (NTC_IS_UDFS_RW( Ntc))  {
    
        ROE( GetFieldValue( Lcb, "udfs!_LCB", "Flags", Ref));
        
        if (LcbDeleted( Ref))  {
        
            dprintf(" *DEL*  ");
        }
    }
#endif

    GetFieldOffset( "udfs!_LCB", "FileName", &Offset);
    DumpStr( Offset, Lcb+Offset, "Name", FALSE, TRUE);



    DumpTreeRecur( Scb, (ULONG)(RecurLevel + 1));
}


void 
DumpCloseQueue( 
    IN ULONG64 Vcb
    )
{
    ULONG64 UdfData; 
    ULONG Offset;

//    ROE( GetFieldOffset( "udfs!", "UdfData", &Offset));
   
//    ROE( GetFieldValue( 0, "Udfs!UdfData", NULL, UdfData));
        
    dprintf("\nDelayed closes on Vcb %I64X\n", Vcb);

    dprintf("NOT IMPLEMENTED\n");
}

// OK
VOID
SummariseIrpContextLite(
    IN ULONG64 RemoteAddress,
    IN LONG Options
    )
{
    ULONG64 Scb;
    USHORT Type;

    ReadM( &Type, RemoteAddress, sizeof( Type));

    ROE( GetFieldValue( RemoteAddress, 
                        "udfs!_IRP_CONTEXT_LITE", 
                        NTC_IS_UDFS_RW( Type) ? "Scb" : "Fcb", Scb));

    dprintf("  0x%I64x\n", Scb);
}

// OK
VOID
SummariseIrpContext(
    IN ULONG64 RemoteAddress,
    IN LONG Options
    )
{
    ULONG64 Irp;

    ROE( GetFieldValue( RemoteAddress, "udfs!_IRP_CONTEXT", "Irp", Irp));

    dprintf("  0x%I64x\n", Irp);
}

// OK
DUMP_ROUTINE( DumpUdfData)
{
    ULONG Count, Offset, LinkOffset;

    Dt( "udfs!UDF_DATA", Address, Options, 0, NULL);

    ROE( GetFieldValue( Address, "udfs!_UDF_DATA", "DelayedCloseCount", Count));

    if (Count)  {
        
        dprintf("\nSCBs on Delayed Close Queue (%d entries)\n", Count);

        ROE( GetFieldOffset( "udfs!_UDF_DATA", "DelayedCloseQueue", &Offset));
        ROE( GetFieldOffset( "udfs!_IRP_CONTEXT_LITE", "DelayedCloseLinks", &LinkOffset));

        DumpList( Address+Offset, SummariseIrpContextLite, LinkOffset, FALSE, 0);
    }

    dprintf(" \n");
    
    GetFieldValue( Address, "udfs!_UDF_DATA", "AsyncCloseCount", Count);

    if (Count)  {

        dprintf("\nF/SCBs on Async Close Queue (%d entries)\n", Count);

        ROE( GetFieldOffset( "udfs!_UDF_DATA", "AsyncCloseQueue", &Offset));
        ROE( GetFieldOffset( "udfs!_IRP_CONTEXT", "WorkQueueItem.List", &LinkOffset));
        
        DumpList( Address+Offset, SummariseIrpContext, LinkOffset, FALSE, 0);
    }
    
    dprintf(" \n");
}

// OK
DUMP_ROUTINE( DumpTreeSummary)
{
    ULONG64 RootFcbAddr, Scb, Vcb = 0;
    ULONG Error;
    USHORT Type;
    
    ReadM( &Type, Address, sizeof( Type));

#ifdef UDFS_RW_IN_BUILD
    if ((UDFS_NTC_VCB == Type) || (UDFSRW_NTC_VCB == Type) ) {
#else
    if (UDFS_NTC_VCB == Type)  {
#endif        
        Error = GetFieldValue( Address, 
                               "udfs!_VCB",
                               NTC_IS_UDFS_RW( Type) ? "RootIndexScb" : "RootIndexFcb", 
                               Scb);
        if (Error)  {
        
            dprintf( "Error %d reading rootindex f/scb addr\n", Error);
            return;
        }

        Vcb = Address;
        dprintf( "\nSummary tree for UDF Vcb @ %I64x    Refs: (clean,userref,ref - linkcount)\n\n", Address );
        dprintf( "(1,0,0) handle\n(0,1,1) Outstanding FO / del.close / child lcb\n(0,0,1) Stream file\n(0,0,1) Stream directory\n\n");
    }
    else {

        //
        //  Try and extract vcb from scb->fcb to dump delay close info
        //

        Scb = Address;
    }
    
    DumpTreeRecur( Scb, 0);

    //
    //  Dump delayed close queue info
    //

    dprintf("\nDo   !d Udfs!UdfData   to display globals and dump the delayed close queue\n");

    dprintf( "\n" );
}


DECLARE_API( tree )
{
    UNREFERENCED_PARAMETER( dwCurrentPc );
    UNREFERENCED_PARAMETER( hCurrentProcess );

    ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpTreeSummary, dwProcessor, hCurrentThread );
}

DECLARE_API( udfvcb )
{
    UNREFERENCED_PARAMETER( dwCurrentPc );
    UNREFERENCED_PARAMETER( hCurrentProcess );

    ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpUdfVcb, dwProcessor, hCurrentThread );
}


DECLARE_API( udficbsc )
{
    UNREFERENCED_PARAMETER( dwCurrentPc );
    UNREFERENCED_PARAMETER( hCurrentProcess );

    ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpUdfIcbSearchContext, dwProcessor, hCurrentThread );
}


DECLARE_API( udfccb)
{
    ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpUdfCcb, dwProcessor, hCurrentThread );
}


DECLARE_API( udffcb )
{
    UNREFERENCED_PARAMETER( dwCurrentPc );
    UNREFERENCED_PARAMETER( hCurrentProcess );

    ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpUdfFcb, dwProcessor, hCurrentThread );
}


DECLARE_API( udfvdo )
{
    UNREFERENCED_PARAMETER( dwCurrentPc );
    UNREFERENCED_PARAMETER( hCurrentProcess );

    ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpUdfVdo, dwProcessor, hCurrentThread );
}



DECLARE_API( udfirpcontext )
{
    UNREFERENCED_PARAMETER( dwCurrentPc );
    UNREFERENCED_PARAMETER( hCurrentProcess );

    ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpUdfIrpContext, dwProcessor, hCurrentThread );
}


DECLARE_API( udfirpcontextlite )
{
    UNREFERENCED_PARAMETER( dwCurrentPc );
    UNREFERENCED_PARAMETER( hCurrentProcess );

    ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpUdfIrpContextLite, dwProcessor, hCurrentThread );
}


DECLARE_API( udfmcb )
{
    UNREFERENCED_PARAMETER( dwCurrentPc );
    UNREFERENCED_PARAMETER( hCurrentProcess );

    ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpLargeMcb, dwProcessor, hCurrentThread );
}


DECLARE_API( lcb )
{
    UNREFERENCED_PARAMETER( dwCurrentPc );
    UNREFERENCED_PARAMETER( hCurrentProcess );

    ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpUdfLcb, dwProcessor, hCurrentThread );
}

DECLARE_API( pcb )
{
    UNREFERENCED_PARAMETER( dwCurrentPc );
    UNREFERENCED_PARAMETER( hCurrentProcess );

    ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpUdfPcb, dwProcessor, hCurrentThread );
}


//
//  RAW ON DISC STUCTURE DUMP ROUTINES FOLLOW....
//

#define UdfNodeTypeName( InfoIndex)  (UdfOnDiscTypeCodes[ InfoIndex].Text)
#define UdfNodeTypeDumpFunction( InfoIndex)  (UdfOnDiscTypeCodes[ InfoIndex].DumpRoutine)
#define UdfNodeTypeSize( InfoIndex)  (UdfOnDiscTypeCodes[ InfoIndex].Size)


#define DUMP_EXTN_AD( type, realaddress, structure, element, label)       \
        dprintf( "\n(%03x) %08x -> %s (LSN %08x, LEN %08x)",              \
                FIELD_OFFSET(type, element),                              \
                ((PUCHAR)realaddress) + FIELD_OFFSET(type, element),      \
                label,                                                    \
                structure.element.Lsn, structure.element.Len)

static UCHAR RegIdBuffer[ 33];

#define DUMP_REGID( type, realaddress, structure, element, label)         \
        strncpy( RegIdBuffer, structure.element.Identifier, 23);          \
        strncpy( RegIdBuffer+24, structure.element.Suffix, 8);            \
        RegIdBuffer[23] = '\0';                                           \
        RegIdBuffer[32] = '\0';                                           \
        dprintf( "\n(%03x) %08x -> %s (Flags %02x, '%s':'%s')",           \
                FIELD_OFFSET(type, element),                              \
                ((PUCHAR)realaddress) + FIELD_OFFSET(type, element),      \
                label,                                                    \
                structure.element.Flags,                                  \
                RegIdBuffer, RegIdBuffer+24)

#define DUMP_LONGAD( type, realaddress, structure, element, label)        \
        dprintf( "\n(%03x) %08x -> %s (Len %1x:%08x, StartLba %04x:%08x)",\
                FIELD_OFFSET(type, element),                              \
                ((PUCHAR)realaddress) + FIELD_OFFSET(type, element),      \
                label,                                                    \
                structure.element.Length.Type,                            \
                structure.element.Length.Length,                          \
                structure.element.Start.Partition,                        \
                structure.element.Start.Lbn)

#define DUMP_NSRLBA( type, realaddress, structure, element, label)        \
        dprintf( "\n(%03x) %08x -> %s (%04x:%08x)",                       \
                FIELD_OFFSET(type, element),                              \
                ((PUCHAR)realaddress) + FIELD_OFFSET(type, element),      \
                label,                                                    \
                structure.element.Partition,                              \
                structure.element.Lbn)


DUMP_ROUTINE( DumpUdfOnDiscStructure)
{
    DESTAG Tag;
    PDESTAG pTag;
    ULONG Result, InfoIndex;
    STRUCT_DUMP_ROUTINE Routine;
    
    RM( Address, Tag, pTag, PDESTAG, Result );
  
    InfoIndex = SearchTypeCodeIndex( Tag.Ident, UdfOnDiscTypeCodes);
    Routine = UdfNodeTypeDumpFunction( InfoIndex);

    if ( NULL == Routine) {
    
        dprintf( "Udf/Iso node tag 0x%04x unknown.\n", Tag.Ident);
        return;
    }

    //
    //  And call it...
    //

    dprintf( "\n%s @ %08x\n", UdfNodeTypeName(InfoIndex),  Address);
    
    (Routine)(Address, Options, &UdfOnDiscTypeCodes[InfoIndex]);
}


DUMP_ROUTINE( DumpUdfOnDiscTag)
{
    DESTAG Tag;
    PDESTAG pTag;
    ULONG Result, InfoIndex;
    STRUCT_DUMP_ROUTINE Routine;
    
    RM( Address, Tag, pTag, PDESTAG, Result );

    InfoIndex = SearchTypeCodeIndex( Tag.Ident, UdfOnDiscTypeCodes);
    Routine = UdfNodeTypeDumpFunction( InfoIndex);

    DUMP16_WITH_OFFSET(  DESTAG, Tag,    Ident,     "Ident" );    
    dprintf(" (== %s)", UdfNodeTypeName(InfoIndex));
    
    DUMP16_WITH_OFFSET(  DESTAG, Tag,    Version,   "Version");
    DUMP8_WITH_OFFSET(   DESTAG, Tag,    Checksum,  "Checksum");
    DUMP16_WITH_OFFSET(  DESTAG, Tag,    Serial,    "Serial");
    DUMP16_WITH_OFFSET(  DESTAG, Tag,    CRC,       "CRC");
    DUMP16_WITH_OFFSET(  DESTAG, Tag,    CRCLen,    "CRCLen");
    DUMP_WITH_OFFSET(    DESTAG, Tag,    Lbn,       "(TagLoc)Lbn");
}


DUMP_ROUTINE( DumpUdfOnDiscPvd)
{
    NSR_PVD Pvd;
    PNSR_PVD pPvd;
    ULONG Result;
    
    RM( Address, Pvd, pPvd, PNSR_PVD, Result );

    DUMP_EMBW_OFFSET(  NSR_PVD, Address,   Destag,              "Destag" );
    DUMP_WITH_OFFSET(  NSR_PVD, Pvd,       VolDescSeqNum,       "VolDescSeqNum" );
    DUMP_WITH_OFFSET(  NSR_PVD, Pvd,       Number,              "Number" );

    Pvd.VolumeID[ Pvd.VolumeID[ 31]] = '\0';
    DUMP_RAW_TERM_STRN_OFFSET( NSR_PVD, Pvd, Address, VolumeID,    "VolumeID");
   
    DUMP16_WITH_OFFSET(  NSR_PVD, Pvd,       VolSetSeq,         "VolSetSeq" );
    DUMP16_WITH_OFFSET(  NSR_PVD, Pvd,       VolSetSeqMax,      "VolSetSeqMax" );
    DUMP16_WITH_OFFSET(  NSR_PVD, Pvd,       Level,             "Level" );
    DUMP16_WITH_OFFSET(  NSR_PVD, Pvd,       LevelMax,          "LevelMax" );

    Pvd.VolSetID[ Pvd.VolSetID[ 127]] = '\0';
    DUMP_RAW_TERM_STRN_OFFSET( NSR_PVD, Pvd, Address, VolSetID,    "VolSetID");
    
    DUMP_EXTN_AD( NSR_PVD, Address, Pvd, Abstract, "Abstract");
    DUMP_EXTN_AD( NSR_PVD, Address, Pvd, Copyright, "Copyright");
    DUMP_REGID( NSR_PVD, Address, Pvd, ImpUseID, "ImpUseID");
    
    DUMP_WITH_OFFSET(  NSR_PVD, Pvd,       Predecessor,    "Predecessor" );
    DUMP16_WITH_OFFSET(  NSR_PVD, Pvd,     Flags,          "Flags" );
}


DUMP_ROUTINE( DumpUdfOnDiscAnchor)
{
    NSR_ANCHOR Avd;
    PNSR_ANCHOR pAvd;
    ULONG Result;
    
    RM( Address, Avd, pAvd, PNSR_ANCHOR, Result );
    
    DUMP_EMBW_OFFSET(  NSR_ANCHOR, Address,   Destag,           "Destag" );
    DUMP_EXTN_AD( NSR_ANCHOR, Address, Avd,   Main,             "Main");
    DUMP_EXTN_AD( NSR_ANCHOR, Address, Avd,   Reserve,          "Reserve");
}


DUMP_ROUTINE( DumpUdfOnDiscVdp)
{
    NSR_VDP Vdp;
    PNSR_VDP pVdp;
    ULONG Result;
    
    RM( Address, Vdp, pVdp, PNSR_VDP, Result );
    
    DUMP_EMBW_OFFSET(  NSR_VDP, Address,   Destag,           "Destag" );
    DUMP_WITH_OFFSET(  NSR_VDP, Vdp,       VolDescSeqNum,    "VolDescSeqNum" );
    DUMP_EXTN_AD( NSR_VDP, Address, Vdp,   Next,             "Next");
}


DUMP_ROUTINE( DumpUdfOnDiscPartDesc)
{
    NSR_PART Part;
    PNSR_PART pPart;
    ULONG Result;
    
    RM( Address, Part, pPart, PNSR_PART, Result );
    
    dprintf("\n(3/10.5)\n");
    
    DUMP_EMBW_OFFSET(  NSR_PART, Address,   Destag,         "Destag" );
    DUMP_WITH_OFFSET(  NSR_PART, Part,      VolDescSeqNum,  "VolDescSeqNum" );

    DUMP16_WITH_OFFSET(  NSR_PART, Part,      Flags,        "Flags" );
    DUMP16_WITH_OFFSET(  NSR_PART, Part,      Number,       "Number" );

    DUMP_REGID( NSR_PART, Address, Part, ContentsID,        "ContentsID");
    
    DUMP_WITH_OFFSET(  NSR_PART, Part,      AccessType,     "AccessType" );
    DUMP_WITH_OFFSET(  NSR_PART, Part,      Start,          "Start" );
    DUMP_WITH_OFFSET(  NSR_PART, Part,      Length,         "Length" );
    
    DUMP_REGID( NSR_PART, Address, Part,    ImpUseID,       "ImpUseID");
}

DUMP_ROUTINE( DumpUdfOnDiscLvol)
{
    NSR_LVOL Lv;
    PNSR_LVOL pLv;
    ULONG Result;
    
    RM( Address, Lv, pLv, PNSR_LVOL, Result );
    
    DUMP_EMBW_OFFSET(  NSR_LVOL, Address,   Destag,         "Destag" );
    DUMP_WITH_OFFSET(  NSR_LVOL, Lv,        VolDescSeqNum,  "VolDescSeqNum" );

    Lv.VolumeID[ Lv.VolumeID[ 127]] = '\0';
    DUMP_RAW_TERM_STRN_OFFSET( NSR_LVOL, Lv, Address, VolumeID,    "VolumeID");
    
    DUMP_WITH_OFFSET(  NSR_LVOL, Lv,        BlockSize,  "BlockSize" );
    DUMP_REGID( NSR_LVOL, Address, Lv,      DomainID,   "DomainID");

    DUMP_LONGAD( NSR_LVOL, Address, Lv, FSD, "FSD");
    
    DUMP_WITH_OFFSET(  NSR_LVOL, Lv,        MapTableLength, "MapTableLength" );
    DUMP_WITH_OFFSET(  NSR_LVOL, Lv,        MapTableCount,  "MapTableCount" );
    
    DUMP_REGID( NSR_LVOL, Address,  Lv, ImpUseID,   "ImpUseID");
    
    DUMP_EXTN_AD( NSR_LVOL, Address, Lv, Integrity,             "Integrity");
    DUMP_EMBW_OFFSET(  NSR_LVOL, Address,   MapTable,           "MapTable" );
}


DUMP_ROUTINE( DumpUdfOnDiscLvInteg)
{
}


DUMP_ROUTINE( DumpUdfOnDiscFsd)
{
    NSR_FSD Fsd;
    PNSR_FSD pFsd;
    ULONG Result;
    STRING Str;
    
    RM( Address, Fsd, pFsd, PNSR_FSD, Result );
    
    DUMP_EMBW_OFFSET(  NSR_FSD, Address,   Destag,          "Destag" );
    DUMP_EMBW_OFFSET(  NSR_FSD, Address,   Time,            "Time" );
    DUMP16_WITH_OFFSET(  NSR_FSD, Fsd,      Level,         "Level" );
    DUMP16_WITH_OFFSET(  NSR_FSD, Fsd,      LevelMax,       "LevelMax" );
    DUMP_WITH_OFFSET(  NSR_FSD, Fsd,      FileSet,       "FileSet" );
    DUMP_WITH_OFFSET(  NSR_FSD, Fsd,      FileSetDesc,       "FileSetDesc" );

    Fsd.VolID[ Fsd.VolID[ 127]] = '\0';
    DUMP_RAW_TERM_STRN_OFFSET( NSR_FSD, Fsd, Address, VolID,       "VolID    ");

    Fsd.FileSetID[ Fsd.FileSetID[ 127]] = '\0';
    DUMP_RAW_TERM_STRN_OFFSET( NSR_FSD, Fsd, Address, FileSetID,   "FileSetID");

    DUMP_LONGAD( NSR_FSD, Address, Fsd, IcbRoot,             "IcbRoot   ");
    DUMP_REGID( NSR_FSD, Address, Fsd, DomainID,             "DomainID  ");
    DUMP_LONGAD( NSR_FSD, Address, Fsd, NextExtent,          "NextExtent");

    dprintf("\n");
}


DUMP_ROUTINE( DumpUdfOnDiscFid)
{
    NSR_FID Fid;
    PNSR_FID pFid;
    ULONG Result;
    UCHAR Buffer[512];
    
    RM( Address, Fid, pFid, PNSR_FID, Result );

    dprintf("\nFid.Flags: ");
    PrintState( UdfFidFlags, Fid.Flags);
    
    DUMP_EMBW_OFFSET(  NSR_FID, Address,   Destag,          "Destag" );
    DUMP16_WITH_OFFSET(  NSR_FID, Fid,     Version,         "Version" );
    DUMP8_WITH_OFFSET(  NSR_FID, Fid,      Flags,           "Flags" );
    DUMP8_WITH_OFFSET(  NSR_FID, Fid,      FileIDLen,       "FileIDLen" );
    DUMP_LONGAD( NSR_FID, Address, Fid, Icb, "Icb");
    DUMP16_WITH_OFFSET(  NSR_FID, Fid,     ImpUseLen,       "ImpUseLen" );
    DUMP_EMBW_OFFSET(  NSR_FID, Address,   ImpUse,          "ImpUse" );
    
    dprintf("\nFileID @ %08x\n", ((PUCHAR)Address) + FIELD_OFFSET( NSR_FID, ImpUse) + Fid.ImpUseLen);    
}


DUMP_ROUTINE( DumpUdfOnDiscIcbFile)
{
    ICBFILE Icb;
    PICBFILE pIcb;
    ULONG Result;
    USHORT AllocType;
    ULONG AllocLength = 0;
    DWORD_PTR AllocDescsRealAddr;
    
    RM( Address, Icb, pIcb, PICBFILE, Result );
    
    dprintf("\nIcb.IcbTag.Flags: ");
    PrintState( UdfIcbTagFlags, Icb.Icbtag.Flags);
    
    DUMP_EMBW_OFFSET(  ICBFILE, Address,   Destag,                      "Destag" );

    // IcbTag embedded structure
    
    DUMP_WITH_OFFSET(    ICBFILE, Icb,       Icbtag.PriorDirectCount,   "Icbtag.PriorDirectCount" );
    
    DUMP16_WITH_OFFSET(  ICBFILE, Icb,       Icbtag.StratType,          "Icbtag.StratType" );
    dprintf(" == %s", (Icb.Icbtag.StratType <= MAX_VALID_ICB_STRAT) ? IcbStrategies[ Icb.Icbtag.StratType ] : "INVALID");
    
    DUMP16_WITH_OFFSET(  ICBFILE, Icb,       Icbtag.StratParm,          "Icbtag.StratParm" );
    DUMP16_WITH_OFFSET(  ICBFILE, Icb,       Icbtag.MaxEntries,         "Icbtag.MaxEntries" );
    
    DUMP16_WITH_OFFSET(  ICBFILE, Icb,       Icbtag.FileType,           "Icbtag.FileType" );
    dprintf(" == %s", (Icb.Icbtag.FileType <= MAX_VALID_ICB_TYPE) ? IcbFileTypes[ Icb.Icbtag.FileType ] : "INVALID");
    
    DUMP_NSRLBA( ICBFILE, Address,  Icb,  Icbtag.IcbParent,             "IcbTag.IcbParent");
    DUMP16_WITH_OFFSET(  ICBFILE, Icb,       Icbtag.Flags,              "Icbtag.Flags" );

    // end icbtag

    DUMP_WITH_OFFSET(    ICBFILE, Icb,       UID,               "UID" );
    DUMP_WITH_OFFSET(    ICBFILE, Icb,       GID,               "GID" );
    DUMP_WITH_OFFSET(    ICBFILE, Icb,       Permissions,       "Permissions" );
    DUMP16_WITH_OFFSET(    ICBFILE, Icb,     LinkCount,         "LinkCount" );
    DUMP8_WITH_OFFSET(    ICBFILE, Icb,      RecordFormat,      "RecordFormat" );
    DUMP8_WITH_OFFSET(    ICBFILE, Icb,      RecordDisplay,     "RecordDisplay" );
    DUMP_WITH_OFFSET(    ICBFILE, Icb,       RecordLength,      "RecordLength" );
    DUMP64_WITH_OFFSET(    ICBFILE, Icb,     InfoLength,        "InfoLength" );
    DUMP64_WITH_OFFSET(    ICBFILE, Icb,     BlocksRecorded,    "BlocksRecorded" );
    DUMP_EMBW_OFFSET(  ICBFILE, Address,     AccessTime,        "AccessTime" );
    DUMP_EMBW_OFFSET(  ICBFILE, Address,     ModifyTime,        "ModifyTime" );
    DUMP_EMBW_OFFSET(  ICBFILE, Address,     AttributeTime,     "AttributeTime" );
    DUMP_WITH_OFFSET(    ICBFILE, Icb,       Checkpoint,        "Checkpoint" );
    
    DUMP_LONGAD( ICBFILE,  Address, Icb,  IcbEA,    "IcbEA");
    DUMP_REGID(  ICBFILE,  Address, Icb,  ImpUseID, "ImpUseID");

    DUMP64_WITH_OFFSET(    ICBFILE, Icb,     UniqueID,          "UniqueID" );
    DUMP_WITH_OFFSET(    ICBFILE, Icb,       EALength,          "EALength" );
    DUMP_WITH_OFFSET(    ICBFILE, Icb,       AllocLength,       "AllocLength" );

    // lazy! *** dump EA list
    
    DUMP_EMBW_OFFSET(  ICBFILE, Address,     EAs,     "EAs[]" );

    AllocDescsRealAddr = ((ULONG)Address) + FIELD_OFFSET( ICBFILE, EAs) + Icb.EALength;
    AllocType = (ICBTAG_F_ALLOC_MASK & Icb.Icbtag.Flags);
    
    dprintf("\n\nAllocation descriptors @ %08x\n\n", AllocDescsRealAddr );

    if ((Options & 1) && (ICBTAG_F_ALLOC_IMMEDIATE != AllocType))  {

        UCHAR Buffer[4*1024];
        PUCHAR CurrentDesc = Buffer;
        ULONG Size;

        if ( sizeof(Buffer) < Icb.AllocLength)  {
        
            dprintf("Buffer too small\n");
            return;
        }
        
        //
        //  Dump allocation descriptors
        //
        
        if (!ReadMemory( AllocDescsRealAddr, Buffer, Icb.AllocLength, &Result))  {
        
            dprintf( "Failed to read memory @%p\n", AllocDescsRealAddr);
            return;
        }

        while ( AllocLength < Icb.AllocLength)  {

            switch (AllocType)  {
            
                case ICBTAG_F_ALLOC_SHORT:
                
                    dprintf( " %08x , %01x:%08x\n", ((PSHORTAD)CurrentDesc)->Start, 
                             ((PSHORTAD)CurrentDesc)->Length.Type,  ((PSHORTAD)CurrentDesc)->Length.Length);
                    Size = sizeof( SHORTAD);
                    break;
                    
                case ICBTAG_F_ALLOC_LONG:  {
                    
                        PLONGAD Ad = (PLONGAD)CurrentDesc;
                        
                        dprintf( " %04x:%08x , %01x:%08x,  ImpUse[6] @ %08x\n", 
                                 Ad->Start.Partition, Ad->Start.Lbn, 
                                 Ad->Length.Type,  Ad->Length.Length,
                                 AllocDescsRealAddr + AllocLength + FIELD_OFFSET( LONGAD, ImpUse));
                        Size = sizeof( LONGAD);
                    }
                    break;
                    
                case ICBTAG_F_ALLOC_EXTENDED:  {
                    
                        PEXTAD Ad = (PEXTAD)CurrentDesc;
                        
                        dprintf( " %04x:%08x , EL: %01x:%08x, RL: %01x:%08x, InfoL: %08x, ImpUse[2] @ %08x\n", 
                                 Ad->Start.Partition, Ad->Start.Lbn, 
                                 Ad->ExtentLen.Type,  Ad->ExtentLen.Length,
                                 Ad->RecordedLen.Type,  Ad->RecordedLen.Length,
                                 Ad->InfoLen,
                                 AllocDescsRealAddr + AllocLength + FIELD_OFFSET( EXTAD, ImpUse));
                        Size = sizeof( EXTAD);
                    }
                    break;
                    
                default:
                    dprintf("INVALID Allocdesc type %d\n", AllocType);
                    return;
            }

            AllocLength += Size;
            CurrentDesc += Size;
        }
    }
}


DUMP_ROUTINE( DumpUdfOnDiscIcbExtFile)
{
    ICBEXTFILE Icb;
    PICBEXTFILE pIcb;
    ULONG Result;
    USHORT AllocType;
    ULONG AllocLength = 0;
    DWORD_PTR AllocDescsRealAddr;
    
    RM( Address, Icb, pIcb, PICBEXTFILE, Result );
    
    dprintf("\nIcb.IcbTag.Flags: ");
    PrintState( UdfIcbTagFlags, Icb.Icbtag.Flags);
    
    DUMP_EMBW_OFFSET(  ICBFILE, Address,   Destag,                      "Destag" );

    // IcbTag embedded structure
    
    DUMP_WITH_OFFSET(    ICBEXTFILE, Icb,       Icbtag.PriorDirectCount,   "Icbtag.PriorDirectCount" );
    
    DUMP16_WITH_OFFSET(  ICBEXTFILE, Icb,       Icbtag.StratType,          "Icbtag.StratType" );
    dprintf(" == %s", (Icb.Icbtag.StratType <= MAX_VALID_ICB_STRAT) ? IcbStrategies[ Icb.Icbtag.StratType ] : "INVALID");
    
    DUMP16_WITH_OFFSET(  ICBEXTFILE, Icb,       Icbtag.StratParm,          "Icbtag.StratParm" );
    DUMP16_WITH_OFFSET(  ICBEXTFILE, Icb,       Icbtag.MaxEntries,         "Icbtag.MaxEntries" );
    
    DUMP16_WITH_OFFSET(  ICBEXTFILE, Icb,       Icbtag.FileType,           "Icbtag.FileType" );
    dprintf(" == %s", (Icb.Icbtag.FileType <= MAX_VALID_ICB_TYPE) ? IcbFileTypes[ Icb.Icbtag.FileType ] : "INVALID");
    
    DUMP_NSRLBA( ICBEXTFILE, Address,  Icb,  Icbtag.IcbParent,             "IcbTag.IcbParent");
    DUMP16_WITH_OFFSET(  ICBEXTFILE, Icb,       Icbtag.Flags,              "Icbtag.Flags" );

    // end icbtag

    DUMP_WITH_OFFSET(    ICBEXTFILE, Icb,       UID,               "UID" );
    DUMP_WITH_OFFSET(    ICBEXTFILE, Icb,       GID,               "GID" );
    DUMP_WITH_OFFSET(    ICBEXTFILE, Icb,       Permissions,       "Permissions" );
    DUMP16_WITH_OFFSET(    ICBEXTFILE, Icb,     LinkCount,         "LinkCount" );
    DUMP8_WITH_OFFSET(    ICBEXTFILE, Icb,      RecordFormat,      "RecordFormat" );
    DUMP8_WITH_OFFSET(    ICBEXTFILE, Icb,      RecordDisplay,     "RecordDisplay" );
    DUMP_WITH_OFFSET(    ICBEXTFILE, Icb,       RecordLength,      "RecordLength" );
    DUMP64_WITH_OFFSET(    ICBEXTFILE, Icb,     InfoLength,        "InfoLength" );
    DUMP64_WITH_OFFSET(    ICBEXTFILE, Icb,     ObjectSize,         "ObjectSize" );
    DUMP64_WITH_OFFSET(    ICBEXTFILE, Icb,     BlocksRecorded,    "BlocksRecorded" );
    DUMP_EMBW_OFFSET(  ICBEXTFILE, Address,     AccessTime,        "AccessTime" );
    DUMP_EMBW_OFFSET(  ICBEXTFILE, Address,     ModifyTime,        "ModifyTime" );
    DUMP_EMBW_OFFSET(  ICBEXTFILE, Address,     AttributeTime,     "AttributeTime" );
    DUMP_EMBW_OFFSET(  ICBEXTFILE, Address,     CreationTime,     "CreationTime" );
    DUMP_WITH_OFFSET(    ICBEXTFILE, Icb,       Checkpoint,        "Checkpoint" );
    
    DUMP_LONGAD( ICBEXTFILE,  Address, Icb,  IcbEA,    "IcbEA");
    DUMP_LONGAD( ICBEXTFILE,  Address, Icb,  IcbStream,    "IcbStream");
    DUMP_REGID(  ICBEXTFILE,  Address, Icb,  ImpUseID, "ImpUseID");

    DUMP64_WITH_OFFSET(    ICBEXTFILE, Icb,     UniqueID,          "UniqueID" );
    DUMP_WITH_OFFSET(    ICBEXTFILE, Icb,       EALength,          "EALength" );
    DUMP_WITH_OFFSET(    ICBEXTFILE, Icb,       AllocLength,       "AllocLength" );

    // lazy! *** dump EA list
    
    DUMP_EMBW_OFFSET(  ICBEXTFILE, Address,     EAs,     "EAs[]" );

    AllocDescsRealAddr = ((ULONG)Address) + FIELD_OFFSET( ICBEXTFILE, EAs) + Icb.EALength;
    AllocType = (ICBTAG_F_ALLOC_MASK & Icb.Icbtag.Flags);
    
    dprintf("\n\nAllocation descriptors @ %08x\n\n", AllocDescsRealAddr );

    if ((Options & 1) && (ICBTAG_F_ALLOC_IMMEDIATE != AllocType))  {

        UCHAR Buffer[4*1024];
        PUCHAR CurrentDesc = Buffer;
        ULONG Size;

        if ( sizeof(Buffer) < Icb.AllocLength)  {
        
            dprintf("Buffer too small\n");
            return;
        }
        
        //
        //  Dump allocation descriptors
        //
        
        if (!ReadMemory( AllocDescsRealAddr, Buffer, Icb.AllocLength, &Result))  {
        
            dprintf( "Failed to read memory @%p\n", AllocDescsRealAddr);
            return;
        }

        while ( AllocLength < Icb.AllocLength)  {

            switch (AllocType)  {
            
                case ICBTAG_F_ALLOC_SHORT:
                
                    dprintf( " %08x , %01x:%08x\n", ((PSHORTAD)CurrentDesc)->Start, 
                             ((PSHORTAD)CurrentDesc)->Length.Type,  ((PSHORTAD)CurrentDesc)->Length.Length);
                    Size = sizeof( SHORTAD);
                    break;
                    
                case ICBTAG_F_ALLOC_LONG:  {
                    
                        PLONGAD Ad = (PLONGAD)CurrentDesc;
                        
                        dprintf( " %04x:%08x , %01x:%08x,  ImpUse[6] @ %08x\n", 
                                 Ad->Start.Partition, Ad->Start.Lbn, 
                                 Ad->Length.Type,  Ad->Length.Length,
                                 AllocDescsRealAddr + AllocLength + FIELD_OFFSET( LONGAD, ImpUse));
                        Size = sizeof( LONGAD);
                    }
                    break;
                    
                case ICBTAG_F_ALLOC_EXTENDED:  {
                    
                        PEXTAD Ad = (PEXTAD)CurrentDesc;
                        
                        dprintf( " %04x:%08x , EL: %01x:%08x, RL: %01x:%08x, InfoL: %08x, ImpUse[2] @ %08x\n", 
                                 Ad->Start.Partition, Ad->Start.Lbn, 
                                 Ad->ExtentLen.Type,  Ad->ExtentLen.Length,
                                 Ad->RecordedLen.Type,  Ad->RecordedLen.Length,
                                 Ad->InfoLen,
                                 AllocDescsRealAddr + AllocLength + FIELD_OFFSET( EXTAD, ImpUse));
                        Size = sizeof( EXTAD);
                    }
                    break;
                    
                default:
                    dprintf("INVALID Allocdesc type %d\n", AllocType);
                    return;
            }

            AllocLength += Size;
            CurrentDesc += Size;
        }
    }
}


DECLARE_API( udftag )
{
    UNREFERENCED_PARAMETER( dwCurrentPc );
    UNREFERENCED_PARAMETER( hCurrentProcess );

    ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpUdfOnDiscTag, dwProcessor, hCurrentThread );
}

DECLARE_API( ud )
{
    UNREFERENCED_PARAMETER( dwCurrentPc );
    UNREFERENCED_PARAMETER( hCurrentProcess );

    ParseAndDump( (PCHAR) args, (STRUCT_DUMP_ROUTINE) DumpUdfOnDiscStructure, dwProcessor, hCurrentThread );
}