//-*-c++-*--------------------------------------------------------------
//  nsrVol.cpp
//   Descriptors for part 3 of ISO/IEC 13346
//
//      Unpublished Confidential Information of Hewlett-Packard
//      Company. Do not disclose.  Copyright (c) Hewlett-Packard
//      Company 1995. All rights reserved
// 
// The contents of this software are proprietary and confidential to
// the Hewlett- Packard Company, and are limited in distribution to
// those with a direct need to know.  Individuals having access to
// this software are responsible for maintaining the confidentiality
// of the content and for keeping the software secure when not in use.
// Transfer to any party is strictly forbidden other than as expressly
// permitted in writing by Hewlett-Packard Company.  Unauthorized
// transfer to or possession by any unauthorized party may be a
// criminal offense.
//
//                  RESTRICTED RIGHTS LEGEND
//
//      Use,  duplication,  or disclosure by the U. S. Government is
//      subject to restrictions as set forth in subparagraph (c) (1)
//      (ii) in the Rights in Technical Data and Computer Software
//      clause in DFARS 252.227-7013 or any other successor clause.
//
//                      HEWLETT-PACKARD COMPANY
//                      3000 Hanover St.
//                      Palo Alto, CA  94304 U.S.A.
//
//      Rights for non-DOD U.S. Government Departments and Agencies
//      are as set forth in FAR 52.227-19 (c) (1,2) or any other
//      successor clause.
//
//  CLASSES:
//   TaggedDescriptor
//   SearchDescriptor
//   ADTable
//   ImplementUseVolumeDesc
//   OSTAVolumeDesc
//   AnchorVolumeDesc
//   PVDFlags
//   PrimaryVolumeDesc
//   PartitionHdrDesc
//   PDFlags
//   PartitionDesc
//   PartitionMap
//   PartMapTable
//   LogicalVolumeDesc
//   UnallocatedSpaceDesc
//   TerminatingDesc
//   FileSetDesc
//   Uint32Table
//   Int32Table
//   LogicalVolumeIntegrityDesc
//  
//----------------------------------------------------------------------
//
// $Source: /projects/HMS/nsr/udfCore/descr/nsrVol.cpp,v $
// $Revision: 1.37 $    $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/06/21 23:36:18 $

#include "nsrLib.h"

// TaggedDescriptor

NSR::TaggedDescriptor::TaggedDescriptor ( TagType t,
                                          UINT16 len ) :
    err( error_None ),
    tag( 0, t) {
    descLen(len);
}

NSR::TaggedDescriptor::~TaggedDescriptor() {}

NSR::Error
NSR::TaggedDescriptor::error() const
{
    if (err == error_None)
        return bodyError();
    else
        return err;
}

void
NSR::TaggedDescriptor::descLen( UINT32 len )
{
    if ( len < tag.field.len() )
        tag.crcLen( 0 );
    else
        tag.crcLen( len - tag.field.len() );
}

void
NSR::TaggedDescriptor::clear()
{
    err = error_None;
    tag.clear();
    bodyClear();
    bodyInit();
}

// NOTE: bodyError() and bodyVerifyISO() are both methods which return an
// error, but DO NOT DIRECTLY CHANGE THE DESCRIPTOR ERROR STATE
// VARIABLE.

// NOTE ALSO that toISO() and fromISO() both set the error state for
// the descriptor if the body has any errors in it. This is to leave a
// record of a failed toISO() or fromISO(), lest the field sizes be
// changed to something more reasonable, clearing out the error state
// before the descriptor error is noticed.

void
NSR::TaggedDescriptor::toISO( BYTE_PTR rbp, UINT32 loc )
{
    if (err == error_None)
        err = bodyError();
    if (err == error_None) {
        bodyToISO( rbp );
        tag.tagLoc( loc );
        tag.toISO( rbp );
#ifdef NSR_VERIFY
        if (debug.verifyISO()) {
            err = verifyISO( rbp, loc );
            if ( err ) debug.holdImage( rbp, descLen() );
        }
#endif
    }
}

void
NSR::TaggedDescriptor::fromISOBlind( CONST_BYTE_PTR rbp )
{
    tag.fromISO( rbp );
    bodyFromISO( rbp );
}

void
NSR::TaggedDescriptor::fromISO( const ByteArray &ba,
                                const UINT32 offs,
                                const UINT32 loc ) 
{
    TagType desiredTagId = tag.tagIdentifier();
    if ( (ba.length() - offs) < tag.field.len() )
        err = error_End_of_buffer;
    CONST_BYTE_PTR rbp = (CONST_BYTE_PTR)ba + offs;
    if ( err == error_None ) {
        tag.fromISO( rbp );
#ifdef NO_TAG_LOCATION_CHECK
        err = tag.isValid( rbp, tag.tagLoc() );
#else
        err = tag.isValid( rbp, loc );
#endif
    }
    if ( err == error_None ) {
        if ((desiredTagId != tag_Unknown) &&
            (desiredTagId != tag.tagIdentifier()))
            err = error_Bad_tag_descriptor_type;
    } else {
        tag.tagIdentifier(desiredTagId); // Restore desired
                                         // descriptor type in
                                         // tagIdentifier field.
    }
    if ( err == error_None ) {
        if ( (ba.length() - offs) < tag.field.len() + tag.crcLen() )
            err = error_End_of_buffer;
    }
    if (err == error_None) {
        err = tag.hasGoodCRC( rbp );     // Only check CRC if Tag is OK.
    }
    if (err == error_None) {
        bodyFromISO( rbp );     // Only translate body if Tag is OK.
        err = bodyError();
    }
}

void
NSR::TaggedDescriptor::fromISO( CONST_BYTE_PTR rbp, UINT32 loc )
{
    TagType desiredTagId = tag.tagIdentifier();
    tag.fromISO( rbp );
    err = tag.isValid( rbp, loc );
    if (err == error_None) {
        if ((desiredTagId != tag_Unknown) &&
            (desiredTagId != tag.tagIdentifier()))
            err = error_Bad_tag_descriptor_type;
    } else {
        tag.tagIdentifier(desiredTagId); // Restore desired
                                         // descriptor type in
                                         // tagIdentifier field.
    }
    if (err == error_None) {
        err = tag.hasGoodCRC( rbp );     // Only check CRC if Tag is OK.
    }
    if (err == error_None) {
        bodyFromISO( rbp );     // Only translate body if Tag is OK.
        err = bodyError();
    }
}

#ifdef NSR_VERIFY
NSR::Error
NSR::TaggedDescriptor::verifyISO( CONST_BYTE_PTR rbp, UINT32 loc )
{
    TaggedDescriptor tmp;
    tmp.fromISO( rbp, loc );
    if ( tmp.error() )
        return tmp.error();
    if ( tmp.tag != tag ) {
        return Error(error_VerifyTag);
    } else {
        return bodyVerifyISO( rbp );
    }
}
#endif

// SearchDescriptor

// fromISOBlind(), fromISOMyopic(), and fromISO()
// NOTE: No checking of CRC, since only tag is xlated.

void
NSR::SearchDescriptor::fromISOBlind( CONST_BYTE_PTR rbp ) {
    tag.fromISO( rbp );
    err = error_None;
}

void
NSR::SearchDescriptor::fromISOMyopic( CONST_BYTE_PTR rbp ) {
    tag.fromISO( rbp );
    // Feed back location so that isValid() will not return location
    // error.
    err = tag.isValid( rbp, tag.tagLoc() );
    if ( err )
        tag.tagIdentifier( tag_Unknown );
}

void
NSR::SearchDescriptor::fromISO( CONST_BYTE_PTR rbp, UINT32 loc ) {
    tag.fromISO( rbp );
    err = tag.isValid( rbp, loc );
    if ( err )
        tag.tagIdentifier( tag_Unknown );
}

void
NSR::SearchDescriptor::bodyClear() {
    tag.clear();
}

// ADTable

NSR::UINT16
NSR::ADTable::maxADs( UINT16 lenADs, ADType at ) const
{
    UINT16            lenOneAD;
    ShortAllocDesc    sad(0);
    LongAllocDesc     lad(0);
    ExtendedAllocDesc ead(0);

    switch ( at ) {
    case ad_Short:
        lenOneAD = sad.field.len();
        break;
    case ad_Long:
        lenOneAD = lad.field.len();
        break;
    case ad_Extended:
        lenOneAD = ead.field.len();
        break;
    case ad_Embedded:
        lenOneAD = 1;
        break;
    }
    return ( lenADs / lenOneAD );
}

#ifdef _INCL_SSOBJS_

//----------------------------------------------------------------------
// METHOD: ADTable::appendToLogExtentList                         --CSB
//      
//  Append to a LogExtentList those LogExtents that mirror (reflect)
//  the internal state of ADs we hold.
//
// ARGS:
//  Partition* partitionp         IN      The partition on which these
//                                        extents exist.
//  LogExtentList& logExtentList  IN/OUT  The extent list to which we append.
//  UINT64 maxInfoLen             IN      A break condition:  do not append
//                                        past a total LogExtentList::infoLen()
//                                        of this number.
//  UINT32& currentIndex          IN/OUT  The begin index in our ADTable we
//                                        start at; re-set on return to be
//                                        the index of the next unread ADTable
//                                        element.
//  Error& err                    OUT     The exit status of this operation.
//
// RETURNS:
//  ADTableStopCondition  The reason we stopped appending to LogExtentList.
// 
// PRE-CONDITIONS:
// 
// POST-CONDITIONS:
// 
// ERRORS:
// 
// NOTES:
//
//----------------------------------------------------------------------
NSR::ADTableStopCondition 
NSR::ADTable::appendToLogExtentList(Partition* partitionp, 
                                    LogExtentList& logExtentList, 
                                    UINT64 maxInfoLen, 
                                    UINT32& currentIndex, 
                                    Error& err)  const
{
    if (Trace::logAlgorithms()) {
        printf("Entering ADTable::appendToLogExtentList():\n");
        printf("  list %d LogExtents (%d infoLen)\n",
               logExtentList.numLogExtents(),
               logExtentList.infoLen());
        printf("  we want max: %d, our table has %d ADs\n",
               maxInfoLen, numADs());
        printf("  and we start at index of %d.\n",
               currentIndex);               
    }

    ADTableStopCondition stop = ad_Nonstop;

    err = error_None;        // No error yet.

    if (logExtentList.error())
    { // If the extent list is already in error.
        err = error_Bad_Parameter_Extent_List;
        if (Trace::logAlgorithms()) {
            printf("Bad parameter: Extent list!\n");
        }
    }
    else if(error()) { 
        // If we are in error.
        err = error();
        if (Trace::logAlgorithms()) {
            printf("We are in Error!  %ld, %s\n", (UINT32)err, err.message());
        }
    } 
    else { 
        // Our parameters are ok.
       
        while((logExtentList.infoLen() < maxInfoLen) && 
              (currentIndex < numADs()) &&
              !err)  {
            // While we have not reached maxInfoLen and the next
            // index is in our ADTable and we don't have an error.
            
            if(isContinued_(currentIndex, err)) {
                // If this is a continuation extent, we can't do
                //   this.  Stop.
                if (Trace::logAlgorithms()) {
                    printf("INDEX IS CONTINUED!  STOP SET TO ad_Continuation!\n");
                }
                stop = ad_Continuation;
                break;  // We can't read this one.
            }

            if(!err) {
                // If we didn't have an error from checking continued.

                if(getExtentLength_(currentIndex, err) == 0) {
                    // If this has a length of zero, then this is
                    // a logical terminating descriptor.  Stop.
                    if (Trace::logAlgorithms()) {
                        printf("EXTENT LENGTH ZERO!  STOP SET TO ad_Terminating!\n");
                    }
                    stop = ad_Terminating;
                    break;  // We can't read this one.
                }
            }

            if(!err) {
                // If we didn't have an error from checking length.

                // Create another LogExtent from the referenced AD and 
                // append it to our LogExtentList.
                LogExtent extent;
                if (Trace::logAlgorithms()) {
                    printf("About to do the toLogExtent_(), index: %d\n", currentIndex);
                }
                toLogExtent_(partitionp, extent, currentIndex, err);
                        
                if(err)
                    // We have an error, so stop.
                    break;
        
                // Append this to our LogExtentList.
                logExtentList.append(extent, err);
        
                currentIndex++;    // We added one more.
            }
        }
    }
    if (Trace::logAlgorithms()) {
        printf("Leaving ADTable::appendToLogExtentList():\n");
        printf("  list has %ld LogExtents (%ld infoLen)\n",
               logExtentList.numLogExtents(),
               logExtentList.infoLen());
        printf("  we want max: %ld, our table has %ld ADs\n",
               maxInfoLen, numADs());
        printf("  and we start at index of %ld; Stop is %ld.\n",
               currentIndex, stop);               
    }
    return stop;
}

//----------------------------------------------------------------------
// METHOD: ADTable::fromLogExtentList                     --CSB
//      
//  Set the ADs in this ADTable with values reflecting the LogExtents
//  in the LogExtentList passed in.  Begin populating the LogExtent
//  at the list index specified, assigning state to the AD at the ADTable
//  index specified.  Continue the process with subsequent extents and
//  ADs until (1) there are no more extents in the LogExtentList, or (2)
//  the ADTable has no more ADs (the ADTable DOES NOT re-size itself).
//
// ARGS:
//  LogExtentList& logExtentList  IN      The extent list we get AD states from.
//  UINT32& currentListIndex      IN/OUT  The begin index in our LogExtentList we
//                                        start at; re-set on return to be
//                                        the index of the next unread LogExtent
//                                        element in this LogExtentList.
//  UINT32 beginADTableIndex      IN      The first AD in our ADTable for which
//                                        we want to set state.
//  Error& err                    OUT     The exit status of this operation.
//
// RETURNS:
//  UINT32  The number of ADs we set state for in our ADTable (reflecting 
//  LogExtents in the LogExtentList).
// 
// PRE-CONDITIONS:
//  This ADTable is assumed to be sized to the appropriate length before
//  this function is called.  We do not re-size the ADTable during this
//  operation.
// 
// POST-CONDITIONS:
// 
// ERRORS:
// 
// NOTES:
//
//----------------------------------------------------------------------
NSR::UINT32 
NSR::ADTable::fromLogExtentList(const LogExtentList& logExtentList,
                                UINT32& currentListIndex, 
                                UINT32 beginADTableIndex,
                                Error& err)
{
    UINT16 numLogExtents = 0;  // We did not append any LogExtents yet.

    err = error_None;        // No error yet.

    if (logExtentList.error())
    { // If the extent list is already in error.
        err = error_Bad_Parameter_Extent_List;
    }
    else if(error())
    { // If we are in error.
        err = error();
    }
       
    // NOTE:  We check for error in each loop pass.

    while((currentListIndex < logExtentList.numLogExtents()) &&
          (beginADTableIndex < numADs()) && 
          !err)  {
        // While we have not reached the end of our LogExtentList
        // and are not at the end of our ADTable, and we don't have
        // an error.
        const LogExtent* extent = logExtentList.at(currentListIndex, err);
            
        if(!err) {
            // If we could get this extent from the list.
            fromLogExtent_(*extent, beginADTableIndex, err);
                
            currentListIndex++;    // We set one more.
            beginADTableIndex++;   // We set one more.
            numLogExtents++;       // We set one more.
        }
    }
    return numLogExtents;
}

//----------------------------------------------------------------------
// METHOD: ADTable::setContinuationLogExtent()                    --CSB
//      
//  Set the AD at this index to be a continuation reference to the
//  extent passed in.  Effectively, the extent passed in is the
//  overflow AED where this AD chain will be continued.
//
// ARGS:
//  Error& err                    OUT     The exit status of this operation.
//
// RETURNS:
// 
// PRE-CONDITIONS:
// 
// POST-CONDITIONS:
// 
// ERRORS:
// 
// NOTES:
//
//----------------------------------------------------------------------
void 
NSR::ADTable::setContinuationLogExtent(const LogExtent& extent,
                                UINT32 adTableIndex,
                                Error& err)
{
    err = error_None;

    // First, we want to set this AD to be a reference to the LogExtent
    //   passed in.
    fromLogExtent_(extent, adTableIndex, err);

    // Now, we want to set this as a continued extent.
    if(!err) {
        // If we were able to set the reference.
        setContinued_(adTableIndex, err);
    }
}

//----------------------------------------------------------------------
// METHOD: ADTable::getContinuationLogExtent()                    --CSB
//      
// ARGS:
//  Error& err                    OUT     The exit status of this operation.
//
// RETURNS:
// 
// PRE-CONDITIONS:
// 
// POST-CONDITIONS:
// 
// ERRORS:
// 
// NOTES:
//
//----------------------------------------------------------------------
void 
NSR::ADTable::getContinuationLogExtent(Partition* partitionp, 
                                       LogExtent& extent,
                                       UINT32 adTableIndex,
                                       Error& err) const
{
    err = error_None;

    if(isContinued_(adTableIndex, err)) {
        // If this extent really is continued.
        if(!err) {
            // If we could determine if we are continued.

            // First, we want to set this AD to be a reference to the LogExtent
            //   passed in.
            toLogExtent_(partitionp, extent, adTableIndex, err);
        }
    } else {
        // We are not a continuation extent!
        // NOTE:  We need to set the error here, not merely return an
        //   empty LogExtent, because the error might have been set
        //   also in the toLogExtent_() operation.
        err = error_NODE_Bad_Continuation_Extent;
    }
}

//----------------------------------------------------------------------
// METHOD: ADTable::setContinued_()                       --CSB
//      
// Force this entry to be a terminating AD entry in this ADTable
// list of ADs.  This means the extent will have a length of zero.
//
// ARGS:
//  Error& err                    OUT     The exit status of this operation.
//
// RETURNS:
// 
// PRE-CONDITIONS:
// 
// POST-CONDITIONS:
// 
// ERRORS:
// 
// NOTES:
//
//----------------------------------------------------------------------
void 
NSR::ADTable::setTerminator(UINT32 adTableIndex, Error& err)
{
    err = error_None;

    // Make an empty extent (will have zero length).
    LogExtent extent;

    // Set the extent at this location in the table.
    fromLogExtent_(extent, adTableIndex, err);

    // We are done.
}

//----------------------------------------------------------------------
// METHOD: ADTable::setContinued_()                       --CSB
//      
// ARGS:
//  Error& err                    OUT     The exit status of this operation.
//
// RETURNS:
// 
// PRE-CONDITIONS:
// 
// POST-CONDITIONS:
// 
// ERRORS:
// 
// NOTES:
//
//----------------------------------------------------------------------
void 
NSR::ADTable::setContinued_(UINT32 adTableIndex, Error& err)
{
    err = error_None;

    if(adTableIndex < numADs()) { 
        // If this index is inside our table.
        switch (adType_) {
        case ad_Short:
            shortADp()[adTableIndex].markContinued();
            break;
        case ad_Long:
            longADp()[adTableIndex].markContinued();
            break;
        case ad_Extended: 
            extendADp()[adTableIndex].markContinued();
            break;
        default:  ;  // null statement
            if (Trace::logAlgorithms()) {
                printf("ERROR:  In ADTable.markContinued_(): Internal ADTable state violation!\n");
            }
        } // switch
    }
    else {
      // We are out of bounds.
      err = error_Index_out_of_bounds;
    }
}

//----------------------------------------------------------------------
// METHOD: ADTable::fromLogExtent()                       --CSB
//      
// ARGS:
//  Error& err                    OUT     The exit status of this operation.
//
// RETURNS:
// 
// PRE-CONDITIONS:
// 
// POST-CONDITIONS:
// 
// ERRORS:
// 
// NOTES:
//
//----------------------------------------------------------------------
void 
NSR::ADTable::fromLogExtent_(const LogExtent& extent, UINT32 adTableIndex, Error& err)
{
    if(adTableIndex < numADs()) { 
        // If this index is inside our table.
        switch (adType_) {
        case ad_Short:
            shortADp()[adTableIndex].fromLogExtent(extent, err);
            break;
        case ad_Long:
            longADp()[adTableIndex].fromLogExtent(extent, err);
            break;
        case ad_Extended: 
            extendADp()[adTableIndex].fromLogExtent(extent, err);
            break;
        default:  ;  // null statement
            if (Trace::logAlgorithms()) {
                printf("ERROR:  In ADTable.fromLogExtent(): Internal ADTable state violation!\n");
            }
        } // switch
    }
}

//----------------------------------------------------------------------
// METHOD: ADTable::toLogExtent_()                        --CSB
//      
// ARGS:
//  Error& err                    OUT     The exit status of this operation.
//
// RETURNS:
// 
// PRE-CONDITIONS:
// 
// POST-CONDITIONS:
// 
// ERRORS:
// 
// NOTES:
//
//----------------------------------------------------------------------
void 
NSR::ADTable::toLogExtent_(Partition* partitionp, LogExtent& extent, 
                           UINT32 adTableIndex, Error& err) const
{
    if (Trace::logAlgorithms()) {
        printf("Enter toLogExtent_, adTableIndex %ld.", adTableIndex);
    }

    if(adTableIndex < numADs()) { 
        // If this index is inside our table.
        switch (adType_) {
        case ad_Short:
            shortADp()[adTableIndex].toLogExtent(partitionp, extent, err);
            break;
        case ad_Long:
            longADp()[adTableIndex].toLogExtent(partitionp->logVolumep(), extent, err);
            break;
        case ad_Extended: 
            extendADp()[adTableIndex].toLogExtent(partitionp->logVolumep(), extent, err);
            break;
        default:  ;  // null statement
            if (Trace::logAlgorithms()) {
                printf("ERROR:  In ADTable.toLogExtent(): Internal ADTable state violation!\n");
            }
        } // switch
    }
    if (Trace::logAlgorithms()) {
        printf("Leaving, length is %d.\n", extent.infoLen());
    }
}

//----------------------------------------------------------------------
// METHOD: ADTable::isContinued_()                        --CSB
//      
// ARGS:
//  Error& err                    OUT     The exit status of this operation.
//
// RETURNS:
// 
// PRE-CONDITIONS:
// 
// POST-CONDITIONS:
// 
// ERRORS:
// 
// NOTES:
//
//----------------------------------------------------------------------
NSR::Boolean 
NSR::ADTable::isContinued_(UINT32 adTableIndex, Error& err) const
{
    Boolean returnVal;

    if(adTableIndex < numADs()) { 
        // If this index is inside our table.
        switch (adType_) {
        case ad_Short:
            returnVal = shortADp()[adTableIndex].isContinued();
            break;
        case ad_Long:
            returnVal = longADp()[adTableIndex].isContinued();
            break;
        case ad_Extended: 
            returnVal = extendADp()[adTableIndex].isContinued();
            break;
        default:  ;  // null statement
            if (Trace::logAlgorithms()) {
                printf("ERROR:  In ADTable.isContinued(): Internal ADTable state violation!\n");
            }
        } // switch
    }
    return returnVal;
}

//----------------------------------------------------------------------
// METHOD: ADTable::getExtentLength_()                    --CSB
//      
// ARGS:
//  Error& err                    OUT     The exit status of this operation.
//
// RETURNS:
// 
// PRE-CONDITIONS:
// 
// POST-CONDITIONS:
// 
// ERRORS:
// 
// NOTES:
//
//----------------------------------------------------------------------
NSR::UINT64 
NSR::ADTable::getExtentLength_(UINT32 adTableIndex, Error& err) const
{
    UINT64 extentLength;

    if(adTableIndex < numADs()) { 
        // If this index is inside our table.
        switch (adType_) {
        case ad_Short:
            extentLength = shortADp()[adTableIndex].extLength();
            break;
        case ad_Long:
            extentLength = longADp()[adTableIndex].extLength();
            break;
        case ad_Extended: 
            extentLength = extendADp()[adTableIndex].extLength();
            break;
        default:  ;  // null statement
            if (Trace::logAlgorithms()) {
                printf("ERROR:  In ADTable.getExtentLength_(): Internal ADTable state violation!\n");
            }
        } // switch
    }
    return extentLength;
}

#endif    // #ifdef _INCL_SSOBJS_

void
NSR::ADTable::allocADs( UINT16 n, ADType at )
{
    shortADp_  = 0;
    longADp_   = 0;
    extendADp_ = 0;

    numADs_ = n;
    adType_ = at;
    // NOTE: the new xxAllocDesc[] MUST be done with more than zero,
    // or the xxADp_->field.len() will access unallocated memory
    // (Purify Array Bounds error)
    switch ( adType_ ) {
    case ad_Short:
        // Do new on at least one! Otherwise, field.len()
        // will cause array bounds error. (found by Purify)
        shortADp_  = new ShortAllocDesc[numADs_?numADs_:1];
        if (shortADp_ != 0)
            field.len( numADs_ * shortADp_->field.len() );
        else
            field.len(0);
        break;
    case ad_Long:
        longADp_   = new LongAllocDesc[numADs_?numADs_:1];
        if (longADp_ != 0)
            field.len( numADs_ * longADp_->field.len() );
        else
            field.len(0);
        break;
    case ad_Extended:
        extendADp_ = new ExtendedAllocDesc[numADs_?numADs_:1];
        if (extendADp_ != 0)
            field.len( numADs_ * extendADp_->field.len() );
        else
            field.len(0);
        break;
    case ad_Embedded:           // numADs_ == number of bytes
        embeddedData_.setLen(numADs_);
        field.len( numADs_ );
        break;
    }
    clear();
}

NSR::ADTable::ADTable( UINT16 o, UINT16 numADs, ADType at ) :
    field( o, 0 ) {         // len filled in by allocADs.
    allocADs( numADs, at );
}

NSR::ADTable::~ADTable() { freeADs(); }

void
NSR::ADTable::dump(CONST_CHAR_PTR  message) const
{
    // NOTE:  We use this stupid pointer to get past "const" problems.
    UINT32 infoLen = 0;

    UINT32 i;
    if(shortADp()) { // If we have short ADs.
        for(i=0; i< numADs(); i++)
        { // For each AD.
            infoLen += shortADp()[i].extLength();
            printf("%ld:(%ld) ", i, shortADp()[i].extLength());
        }
    } else if(longADp()) { // If we have long ADs.
        for(i=0; i< numADs(); i++)
        { // For each AD.
            infoLen += longADp()[i].extLength();
            printf("%ld:(%ld) ", i, longADp()[i].extLength());
        }
    }

    printf("\n%s: infoLen: %ld, Type: %ld, num: %ld", 
           message,infoLen,adType(), numADs());
    printf(" short: %lx, long: %lx, extended: %lx", 
           shortADp(),longADp(),extendADp());
    if (embeddedData_.length() != 0)
        printf(" embedded: [%c]\n",((CONST_BYTE_PTR)embeddedData_)[0]);
    printf("\n");
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::ADTable::operator != ( const ADTable & obj) const
{
    UINT16 bad = 0;

    bad += ( numADs_ != obj.numADs_ );
    unsigned i;
    switch ( adType_ ) {
    case ad_Short:
        if (shortADp_ == 0 || obj.shortADp_ == 0 )
            bad += 1000;
        else
            for ( i = 0; i < numADs_; i++ )
                bad += ( shortADp_[i] != obj.shortADp_[i] );
        break;
    case ad_Long:
        if (longADp_ == 0 || obj.longADp_ == 0 )
            bad += 1000;
        else
            for ( i = 0; i < numADs_; i++ )
                bad += ( longADp_[i] != obj.longADp_[i] );
        break;
    case ad_Extended:
        if (extendADp_ == 0 || obj.extendADp_ == 0 )
            bad += 1000;
        else
            for ( i = 0; i < numADs_; i++ )
                bad += ( extendADp_[i] != obj.extendADp_[i] );
        break;
    case ad_Embedded:
        if ( embeddedData_ != obj.embeddedData_ ) {
            bad += 2000;
        }
        break;
    case ad_Unknown:
        bad += 4000;
    }
    return bad != 0 ? True : False;
}
#endif

NSR::Error
NSR::ADTable::error() const
{
    if ( numADs_ != 0 &&
         ((adType_ == ad_Short && shortADp_ == 0) ||
          (adType_ == ad_Long && longADp_ == 0) ||
          (adType_ == ad_Extended && extendADp_ == 0))) {
        return Error(error_No_free_store);
    } else {
        return embeddedData_.error();
    }
}

void
NSR::ADTable::clear()
{
    unsigned i;
    switch ( adType_ ) {
    case ad_Short:
        if (shortADp_ != 0)
            for ( i = 0; i < numADs_; i++ )
                shortADp_[i].clear();
        break;
    case ad_Long:
        if (longADp_ != 0)
            for ( i = 0; i < numADs_; i++ )
                longADp_[i].clear();
        break;
    case ad_Extended:
        if (extendADp_ != 0)
            for ( i = 0; i < numADs_; i++ )
                extendADp_[i].clear();
        break;
    case ad_Embedded:
        embeddedData_.clear();
    }
}

void
NSR::ADTable::fitToLen( UINT16 len, ADType at )
{
    if ( maxADs(len,at) != numADs_ || adType_ != at ) {
        freeADs();
        allocADs(maxADs(len,at),at);
    } else {
        clear();
    }
}

void
NSR::ADTable::fitToLen( UINT16 len ) // same allocDescType.
{
    UINT16 max = maxADs(len,adType_);
    if ( max != numADs_ ) {
        freeADs();
        allocADs(max,adType_);
    } else {
        clear();
    }
}

void
NSR::ADTable::fitEnts( UINT16 ne, ADType at )
{
    if (ne != numADs_ || adType_ != at) {
        freeADs();
        allocADs(ne, at);
    } else {
        clear();
    } 
}

void
NSR::ADTable::toISO( BYTE_PTR rbp )
{
    rbp += field.start();

    unsigned i;
    switch ( adType_ ) {
    case ad_Short:
        if (shortADp_ != 0)
            for ( i = 0; i < numADs_; i++ ) {
                shortADp_[i].toISO(rbp);
                rbp += shortADp_->field.len();
            }
        break;
    case ad_Long:
        if (longADp_ != 0)
            for ( i = 0; i < numADs_; i++ ) {
                longADp_[i].toISO(rbp);
                rbp += longADp_->field.len();
            }
        break;
    case ad_Extended:
        if (extendADp_ != 0)
            for ( i = 0; i < numADs_; i++ ) {
                extendADp_[i].toISO(rbp);
                rbp += extendADp_->field.len();
            }
        break;
    case ad_Embedded:
        embeddedData_.toMem(rbp);
    }
}

void
NSR::ADTable::fromISO( CONST_BYTE_PTR rbp )
{
    rbp += field.start();

    unsigned i;
    switch ( adType_ ) {
    case ad_Short:
        if (shortADp_ != 0)
            for ( i = 0; i < numADs_; i++ ) {
                shortADp_[i].fromISO(rbp);
                rbp += shortADp_->field.len();
            }
        break;
    case ad_Long:
        if (longADp_ != 0)
            for ( i = 0; i < numADs_; i++ ) {
                longADp_[i].fromISO(rbp);
                rbp += longADp_->field.len();
            }
        break;
    case ad_Extended:
        if (extendADp_ != 0)
            for ( i = 0; i < numADs_; i++ ) {
                extendADp_[i].fromISO(rbp);
                rbp += extendADp_->field.len();
            }
        break;
    case ad_Embedded:
        embeddedData_.fromMem(rbp);
    }
}

// ImplementUseVolumeDesc

NSR::ImplementUseVolumeDesc::ImplementUseVolumeDesc() :

    TaggedDescriptor( tag_IUVD, 512 ),
    volDescSeqNo ( 16 ),
    implementID  ( 20 ),
    implementUse ( 52, 460) {

    bodyInit();
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::ImplementUseVolumeDesc::operator != ( const ImplementUseVolumeDesc& obj ) const
{
    UINT16 bad = 0;

    bad += ( volDescSeqNo != obj.volDescSeqNo );
    bad += ( implementID  != obj.implementID  );
    bad += ( implementUse != obj.implementUse );

    return bad != 0 ? True : False;
}

NSR::Error
NSR::ImplementUseVolumeDesc::bodyVerifyISO( CONST_BYTE_PTR rbp )
{
    ImplementUseVolumeDesc tmp;
    tmp.bodyFromISO(rbp);
    if (tmp != *this) {
        return Error(error_VerifyIUVD);
    } else {
        return Error(error_None);
    }
}
#endif

void
NSR::ImplementUseVolumeDesc::bodyClear()
{
    volDescSeqNo .clear();
    implementID  .clear();
    implementUse .clear();
}

void
NSR::ImplementUseVolumeDesc::bodyInit()
{
    implementID    .descVersionNo(descVersion_);
}

void
NSR::ImplementUseVolumeDesc::bodyToISO( BYTE_PTR rbp )
{
    volDescSeqNo .toISO( rbp );
    implementID  .toISO( rbp );
    implementUse .toISO( rbp );
}

void
NSR::ImplementUseVolumeDesc::bodyFromISO( CONST_BYTE_PTR rbp )
{
    volDescSeqNo .fromISO( rbp );
    implementID  .fromISO( rbp );
    implementUse .fromISO( rbp );
}

// OSTAVolumeDesc

NSR::OSTAVolumeDesc::OSTAVolumeDesc() :

    TaggedDescriptor( tag_IUVD, 512 ),
    volDescSeqNo       ( 16 ),
    OSTAImplID         ( 20 ),
    descCharSet        ( 52 ),
    logicalVolumeID    ( 116, 128 ),
    logicalVolumeInfo1 ( 244, 36 ),
    logicalVolumeInfo2 ( 280, 36 ),
    logicalVolumeInfo3 ( 316, 36 ),
    implementID        ( 352 ),
    implementUse       ( 384, 128 )
{
    bodyInit();
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::OSTAVolumeDesc::operator != ( const OSTAVolumeDesc& obj ) const
{
    UINT16 bad = 0;

    bad += ( volDescSeqNo       != obj.volDescSeqNo       );
    bad += ( OSTAImplID         != obj.OSTAImplID         );
    bad += ( descCharSet        != obj.descCharSet        );
    bad += ( logicalVolumeID    != obj.logicalVolumeID    );
    bad += ( logicalVolumeInfo1 != obj.logicalVolumeInfo1 );
    bad += ( logicalVolumeInfo2 != obj.logicalVolumeInfo2 );
    bad += ( logicalVolumeInfo3 != obj.logicalVolumeInfo3 );
    bad += ( implementID        != obj.implementID        );
    bad += ( implementUse       != obj.implementUse       );

    return bad != 0 ? True : False;
}

NSR::Error
NSR::OSTAVolumeDesc::bodyVerifyISO( CONST_BYTE_PTR rbp )
{
    OSTAVolumeDesc tmp;
    tmp.bodyFromISO(rbp);
    if (tmp != *this) {
        return Error(error_VerifyIUVD);
    } else {
        return Error(error_None);
    }
}
#endif

NSR::Error
NSR::OSTAVolumeDesc::bodyError() const
{
    Error err;

    if (( err = OSTAImplID         .error()) ) return err;
    if (( err = descCharSet        .error()) ) return err;
    if (( err = logicalVolumeID    .error()) ) return err;
    if (( err = logicalVolumeInfo1 .error()) ) return err;
    if (( err = logicalVolumeInfo2 .error()) ) return err;
    if (( err = logicalVolumeInfo3 .error()) ) return err;
    if (( err = implementID        .error()) ) return err;
    if (( err = implementUse       .error()) ) return err;
    return err;
}

void
NSR::OSTAVolumeDesc::bodyClear()
{
    volDescSeqNo       .clear();
    OSTAImplID         .clear();
    descCharSet        .clear();
    logicalVolumeID    .clear();
    logicalVolumeInfo1 .clear();
    logicalVolumeInfo2 .clear();
    logicalVolumeInfo3 .clear();
    implementID        .clear();
    implementUse       .clear();
}

void
NSR::OSTAVolumeDesc::bodyInit()
{
    OSTAImplID         .id( UDFLVInfoID ); // OSTA 2.2.8.1
    implementID        .descVersionNo(descVersion_);
}

void
NSR::OSTAVolumeDesc::bodyToISO( BYTE_PTR rbp )
{
    volDescSeqNo       .toISO( rbp );
    OSTAImplID         .toISO( rbp );
    descCharSet        .toISO( rbp );
    logicalVolumeID    .toISO( rbp );
    logicalVolumeInfo1 .toISO( rbp );
    logicalVolumeInfo2 .toISO( rbp );
    logicalVolumeInfo3 .toISO( rbp );
    implementID        .toISO( rbp );
    implementUse       .toISO( rbp );
}

void
NSR::OSTAVolumeDesc::bodyFromISO( CONST_BYTE_PTR rbp )
{
    volDescSeqNo       .fromISO( rbp );
    OSTAImplID         .fromISO( rbp );
    descCharSet        .fromISO( rbp );
    logicalVolumeID    .fromISO( rbp, descCharSet.charSetType() );
    logicalVolumeInfo1 .fromISO( rbp, descCharSet.charSetType() );
    logicalVolumeInfo2 .fromISO( rbp, descCharSet.charSetType() );
    logicalVolumeInfo3 .fromISO( rbp, descCharSet.charSetType() );
    implementID        .fromISO( rbp );
    implementUse       .fromISO( rbp );
}

// AnchorVolumeDesc

NSR::AnchorVolumeDesc::AnchorVolumeDesc() : 

    TaggedDescriptor( tag_AVD, 512 ),
    mainVolDescSeqExt ( 16 ),
    resvVolDescSeqExt ( 24 ),
    reserved          ( 32, 480) {

}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::AnchorVolumeDesc::operator != ( const AnchorVolumeDesc & obj ) const
{
    UINT16 bad = 0;

    bad += ( mainVolDescSeqExt != obj.mainVolDescSeqExt  );
    bad += ( resvVolDescSeqExt != obj.resvVolDescSeqExt  );
    bad += ( reserved          != obj.reserved           );

    return bad != 0 ? True : False;
}

NSR::Error
NSR::AnchorVolumeDesc::bodyVerifyISO( CONST_BYTE_PTR rbp )
{
    AnchorVolumeDesc tmp;
    tmp.bodyFromISO(rbp);
    if (tmp != *this) {
        return Error(error_VerifyAVD);
    } else {
        return Error(error_None);
    }
}
#endif

NSR::Error
NSR::AnchorVolumeDesc::bodyError() const
{
    return Error(error_None);
}

void
NSR::AnchorVolumeDesc::bodyClear()
{
    mainVolDescSeqExt .clear();
    resvVolDescSeqExt .clear();
    reserved          .clear();

}

void
NSR::AnchorVolumeDesc::bodyToISO( BYTE_PTR rbp )
{
    mainVolDescSeqExt .toISO(rbp);
    resvVolDescSeqExt .toISO(rbp);
    reserved          .toISO(rbp);

}

void
NSR::AnchorVolumeDesc::bodyFromISO( CONST_BYTE_PTR rbp )
{
    mainVolDescSeqExt .fromISO(rbp);
    resvVolDescSeqExt .fromISO(rbp);
    reserved          .fromISO(rbp);

}

// PVDFlags

NSR::PVDFlags::PVDFlags( UINT16 o ) :
    field(o,2),
    commonVolSetIdent ( 0 ),
    reserved          ( 1, 15 ) {
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::PVDFlags::operator != ( const PVDFlags & obj ) const
{
    UINT16 bad = 0;

    bad += ( commonVolSetIdent != obj.commonVolSetIdent  );
    bad += ( reserved          != obj.reserved           );

    return bad != 0 ? True : False;
}
#endif

void
NSR::PVDFlags::clear()
{
    commonVolSetIdent .clear();
    reserved          .clear();
}

void
NSR::PVDFlags::toISO( BYTE_PTR rbp )
{
    rbp += field.start();

    commonVolSetIdent .toISO( rbp );
    reserved          .toISO( rbp );
}

void
NSR::PVDFlags::fromISO( CONST_BYTE_PTR rbp )
{
    rbp += field.start();

    commonVolSetIdent .fromISO( rbp );
    reserved          .fromISO( rbp );
}

// PrimaryVolumeDesc

NSR::PrimaryVolumeDesc::PrimaryVolumeDesc() : 

    TaggedDescriptor(tag_PVD, 512),
    volDescSeqNo     (  16 ),
    priVolDescNo     (  20 ),
    volumeIdentifier (  24, 32 ),
    volumeSeqNo      (  56 ),
    maxVolumeSeqNo   (  58 ),
    interchgLvl      (  60 ),
    maxInterchgLvl   (  62 ),
    charSetList      (  64 ),
    maxCharSetList   (  68 ),
    volumeSetID      (  72, 128 ),
    descCharSet      ( 200 ),
    explCharSet      ( 264 ),
    volAbstract      ( 328 ),
    volCopyright     ( 336 ),
    applicationID    ( 344 ),
    recordDateTime   ( 376 ),
    implementID      ( 388 ),
    implementUse     ( 420, 64 ),
    preVolDescSeqLoc ( 484 ),
    flags            ( 488 ),
    reserved         ( 490, 22 )
{
    bodyInit();
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::PrimaryVolumeDesc::operator != ( const PrimaryVolumeDesc & obj ) const
{
    UINT16 bad = 0;

    bad += ( volDescSeqNo     != obj.volDescSeqNo      );
    bad += ( priVolDescNo     != obj.priVolDescNo      );
    bad += ( volumeIdentifier != obj.volumeIdentifier  );
    bad += ( volumeSeqNo      != obj.volumeSeqNo       );
    bad += ( maxVolumeSeqNo   != obj.maxVolumeSeqNo    );
    bad += ( interchgLvl      != obj.interchgLvl       );
    bad += ( maxInterchgLvl   != obj.maxInterchgLvl    );
    bad += ( charSetList      != obj.charSetList       );
    bad += ( maxCharSetList   != obj.maxCharSetList    );
    bad += ( volumeSetID      != obj.volumeSetID       );
    bad += ( descCharSet      != obj.descCharSet       );
    bad += ( explCharSet      != obj.explCharSet       );
    bad += ( volAbstract      != obj.volAbstract       );
    bad += ( volCopyright     != obj.volCopyright      );
    bad += ( applicationID    != obj.applicationID     );
    bad += ( recordDateTime   != obj.recordDateTime    );
    bad += ( implementID      != obj.implementID       );
    bad += ( implementUse     != obj.implementUse      );
    bad += ( preVolDescSeqLoc != obj.preVolDescSeqLoc  );
    bad += ( flags            != obj.flags             );
    bad += ( reserved         != obj.reserved          );

    return bad != 0 ? True : False;
}

NSR::Error
NSR::PrimaryVolumeDesc::bodyVerifyISO( CONST_BYTE_PTR rbp )
{
    PrimaryVolumeDesc tmp;
    tmp.bodyFromISO(rbp);
    if (tmp != *this) {
        return Error(error_VerifyPVD);
    } else {
        return Error(error_None);
    }
}
#endif

NSR::Error
NSR::PrimaryVolumeDesc::bodyError() const
{
    Error err;

    if (( err = volumeIdentifier .error()) ) return err;
    if (( err = volumeSetID      .error()) ) return err;
    if (( err = descCharSet      .error()) ) return err;
    if (( err = explCharSet      .error()) ) return err;
    if (( err = applicationID    .error()) ) return err;
    if (( err = implementID      .error()) ) return err;
    return err;
}

void
NSR::PrimaryVolumeDesc::bodyClear()
{
    volDescSeqNo     .clear();
    priVolDescNo     .clear();
    volumeIdentifier .clear();
    volumeSeqNo      .clear();
    maxVolumeSeqNo   .clear();
    interchgLvl      .clear();
    maxInterchgLvl   .clear();
    charSetList      .clear();
    maxCharSetList   .clear();
    volumeSetID      .clear();
    descCharSet      .clear();
    explCharSet      .clear();
    volAbstract      .clear();
    volCopyright     .clear();
    applicationID    .clear();
    recordDateTime   .clear();
    implementID      .clear();
    implementUse     .clear();
    preVolDescSeqLoc .clear();
    flags            .clear();
    reserved         .clear();
}

void
NSR::PrimaryVolumeDesc::bodyInit()
{
    volumeIdentifier = "empty";
    interchgLvl      = 3;          // OSTA 2.2.2.1
    maxInterchgLvl   = 3;          // OSTA 2.2.2.2
    charSetList      = 0x00000001; // OSTA 2.2.2.3
    maxCharSetList   = 0x00000001; // OSTA 2.2.2.4
    recordDateTime   = time(0);
    implementID      .descVersionNo(descVersion_);
    flags            .commonVolSetIdent = True;
}

void
NSR::PrimaryVolumeDesc::bodyToISO( BYTE_PTR bp)
{
    volDescSeqNo     .toISO( bp );
    priVolDescNo     .toISO( bp );
    volumeIdentifier .toISO( bp );
    volumeSeqNo      .toISO( bp );
    maxVolumeSeqNo   .toISO( bp );
    interchgLvl      .toISO( bp );
    maxInterchgLvl   .toISO( bp );
    charSetList      .toISO( bp );
    maxCharSetList   .toISO( bp );
    volumeSetID      .toISO( bp );
    descCharSet      .toISO( bp );
    explCharSet      .toISO( bp );
    volAbstract      .toISO( bp );
    volCopyright     .toISO( bp );
    applicationID    .toISO( bp );
    recordDateTime   .toISO( bp );
    implementID      .toISO( bp );
    implementUse     .toISO( bp );
    preVolDescSeqLoc .toISO( bp );
    flags            .toISO( bp );
    reserved         .toISO( bp );
}

void
NSR::PrimaryVolumeDesc::bodyFromISO( CONST_BYTE_PTR bp )
{
    volDescSeqNo     .fromISO( bp );
    priVolDescNo     .fromISO( bp );
    descCharSet      .fromISO( bp ); // OUT OF ORDER
    volumeIdentifier .fromISO( bp, descCharSet.charSetType() );
    volumeSeqNo      .fromISO( bp );
    maxVolumeSeqNo   .fromISO( bp );
    interchgLvl      .fromISO( bp );
    maxInterchgLvl   .fromISO( bp );
    charSetList      .fromISO( bp );
    maxCharSetList   .fromISO( bp );
    volumeSetID      .fromISO( bp, descCharSet.charSetType() );
//  descCharSet      .fromISO( bp ); // Done EARLY -- see above
    explCharSet      .fromISO( bp );
    volAbstract      .fromISO( bp );
    volCopyright     .fromISO( bp );
    applicationID    .fromISO( bp );
    recordDateTime   .fromISO( bp );
    implementID      .fromISO( bp );
    implementUse     .fromISO( bp );
    preVolDescSeqLoc .fromISO( bp );
    flags            .fromISO( bp );
    reserved         .fromISO( bp );
}

// PartitionHdrDesc

NSR::PartitionHdrDesc::PartitionHdrDesc(UINT16 o) :

    field(o,128),
    unallocSpaceTable  (  0 ),
    unallocSpaceBitmap (  8 ),
    partIntegrityTable ( 16 ),
    freedSpaceTable    ( 24 ),
    freedSpaceBitmap   ( 32 ),
    reserved           ( 40, 88 )
{}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::PartitionHdrDesc::operator != ( const PartitionHdrDesc & obj ) const
{
    UINT16 bad = 0;

    bad += ( unallocSpaceTable  != obj.unallocSpaceTable   );
    bad += ( unallocSpaceBitmap != obj.unallocSpaceBitmap  );
    bad += ( partIntegrityTable != obj.partIntegrityTable  );
    bad += ( freedSpaceTable    != obj.freedSpaceTable     );
    bad += ( freedSpaceBitmap   != obj.freedSpaceBitmap    );
    bad += ( reserved           != obj.reserved            );

    return bad != 0 ? True : False;
}
#endif

void
NSR::PartitionHdrDesc::clear()
{
    unallocSpaceTable  .clear();
    unallocSpaceBitmap .clear();
    partIntegrityTable .clear();
    freedSpaceTable    .clear();
    freedSpaceBitmap   .clear();
    reserved           .clear();
}

void
NSR::PartitionHdrDesc::toISO( BYTE_PTR rbp )
{
    rbp += field.start();

    unallocSpaceTable  .toISO( rbp );
    unallocSpaceBitmap .toISO( rbp );
    partIntegrityTable .toISO( rbp );
    freedSpaceTable    .toISO( rbp );
    freedSpaceBitmap   .toISO( rbp );
    reserved           .toISO( rbp );
}

void
NSR::PartitionHdrDesc::fromISO( CONST_BYTE_PTR rbp )
{
    rbp += field.start();

    unallocSpaceTable  .fromISO( rbp );
    unallocSpaceBitmap .fromISO( rbp );
    partIntegrityTable .fromISO( rbp );
    freedSpaceTable    .fromISO( rbp );
    freedSpaceBitmap   .fromISO( rbp );
    reserved           .fromISO( rbp );
}

// PDFlags

NSR::PDFlags::PDFlags( UINT16 o ) :
    field(o,2),
    allocation ( 0 ),
    reserved   ( 1, 15 ) {
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::PDFlags::operator != ( const PDFlags & obj ) const
{
    UINT16 bad = 0;

    bad += ( allocation != obj.allocation  );
    bad += ( reserved   != obj.reserved    );

    return bad != 0 ? True : False;
}
#endif

void
NSR::PDFlags::clear()
{
    allocation .clear();
    reserved   .clear();
}

void
NSR::PDFlags::toISO( BYTE_PTR rbp )
{
    rbp += field.start();

    allocation .toISO( rbp );
    reserved   .toISO( rbp );
}

void
NSR::PDFlags::fromISO( CONST_BYTE_PTR rbp )
{
    rbp += field.start();

    allocation .fromISO( rbp );
    reserved   .fromISO( rbp );
}

// PartitionDesc

NSR::PartitionDesc::PartitionDesc() : 

    TaggedDescriptor(tag_PD, 512),
    volDescSeqNo      (  16 ),
    partitionFlags    (  20 ),
    partitionNo       (  22 ),
    partitionContents (  24 ),
    partitionHdrDesc  (  56 ),
    accessType        ( 184 ),
    partStartLoc      ( 188 ),
    partLength        ( 192 ),
    implementID       ( 196 ),
    implementUse      ( 228, 128 ),
    reserved          ( 356, 156 )
{
    bodyInit();
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::PartitionDesc::operator != ( const PartitionDesc & obj ) const
{
    UINT16 bad = 0;

    bad += ( volDescSeqNo      != obj.volDescSeqNo       );
    bad += ( partitionFlags    != obj.partitionFlags     );
    bad += ( partitionNo       != obj.partitionNo        );
    bad += ( partitionContents != obj.partitionContents  );
    bad += ( partitionHdrDesc  != obj.partitionHdrDesc   );
    bad += ( accessType        != obj.accessType         );
    bad += ( partStartLoc      != obj.partStartLoc       );
    bad += ( partLength        != obj.partLength         );
    bad += ( implementID       != obj.implementID        );
    bad += ( implementUse      != obj.implementUse       );
    bad += ( reserved          != obj.reserved           );

    return bad != 0 ? True : False;
}

NSR::Error
NSR::PartitionDesc::bodyVerifyISO( CONST_BYTE_PTR rbp )
{
    PartitionDesc tmp;
    tmp.bodyFromISO(rbp);
    if (tmp != *this) {
        return Error(error_VerifyPD);
    } else {
        return Error(error_None);
    }
}
#endif

NSR::Error
NSR::PartitionDesc::bodyError() const
{
    Error err;

    if (( err = partitionContents .error()) ) return err;
    if (( err = implementID       .error()) ) return err;
    if (( err = implementUse      .error()) ) return err;
    return err;
}

void
NSR::PartitionDesc::bodyClear()
{
    volDescSeqNo      .clear();
    partitionFlags    .clear();
    partitionNo       .clear();
    partitionContents .clear();
    partitionHdrDesc  .clear();
    accessType        .clear();
    partStartLoc      .clear();
    partLength        .clear();
    implementID       .clear();
    implementUse      .clear();
    reserved          .clear();
}

void
NSR::PartitionDesc::bodyInit()
{
    partitionFlags    .allocation = True;
    partitionContents .id(String("+NSR02"));
    accessType        = Rewritable;
    implementID       .descVersionNo(descVersion_);
}

void
NSR::PartitionDesc::bodyToISO( BYTE_PTR rbp )
{
    volDescSeqNo      .toISO( rbp );
    partitionFlags    .toISO( rbp );
    partitionNo       .toISO( rbp );
    partitionContents .toISO( rbp );
    partitionHdrDesc  .toISO( rbp );
    accessType        .toISO( rbp );
    partStartLoc      .toISO( rbp );
    partLength        .toISO( rbp );
    implementID       .toISO( rbp );
    implementUse      .toISO( rbp );
    reserved          .toISO( rbp );
}

void
NSR::PartitionDesc::bodyFromISO( CONST_BYTE_PTR rbp )
{
    volDescSeqNo      .fromISO( rbp );
    partitionFlags    .fromISO( rbp );
    partitionNo       .fromISO( rbp );
    partitionContents .fromISO( rbp );
    partitionHdrDesc  .fromISO( rbp );
    accessType        .fromISO( rbp );
    partStartLoc      .fromISO( rbp );
    partLength        .fromISO( rbp );
    implementID       .fromISO( rbp );
    implementUse      .fromISO( rbp );
    reserved          .fromISO( rbp );
}

// PartitionMap

void
NSR::PartitionMap::init()
{
    partitionMapType   = 1;       // OSTA 2.2.4.6
    partitionMapLength = 6;       // implied by above
}

NSR::PartitionMap::PartitionMap( UINT16 o ):

    field(o,6),
    partitionMapType   ( 0 ),
    partitionMapLength ( 1 ),
    volumeSeqNo        ( 2 ),
    partitionNo        ( 4 )
{
    init();
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::PartitionMap::operator != ( const PartitionMap & obj ) const
{
    UINT16 bad = 0;

    bad += ( partitionMapType   != obj.partitionMapType    );
    bad += ( partitionMapLength != obj.partitionMapLength  );
    bad += ( volumeSeqNo        != obj.volumeSeqNo         );
    bad += ( partitionNo        != obj.partitionNo         );

    return bad != 0 ? True : False;
}
#endif

void
NSR::PartitionMap::clear()
{
    partitionMapType   .clear();
    partitionMapLength .clear();
    volumeSeqNo        .clear();
    partitionNo        .clear();

    init();
}

void
NSR::PartitionMap::toISO( BYTE_PTR rbp )
{
    rbp += field.start();

    partitionMapType   .toISO( rbp );
    partitionMapLength .toISO( rbp );
    volumeSeqNo        .toISO( rbp );
    partitionNo        .toISO( rbp );
}

void
NSR::PartitionMap::fromISO( CONST_BYTE_PTR rbp )
{
    rbp += field.start();

    partitionMapType   .fromISO( rbp );
    partitionMapLength .fromISO( rbp );
    volumeSeqNo        .fromISO( rbp );
    partitionNo        .fromISO( rbp );
}

void
NSR::PartMapTable::allocEnts( UINT16 ne )
{
    entry   = new PartitionMap[ne?ne:0]; // Never allocate zero
    numEnts = ne;
    if (entry != 0)
        field.len( ne * entry->field.len() );
    else
        field.len(0);
    clear();
}

NSR::PartMapTable::PartMapTable( UINT16 o, UINT16 ne ) :
    field( o, 0 ) {         // len filled in by allocEnts.
    allocEnts( ne );
}

NSR::PartMapTable::~PartMapTable() { freeEnts(); }

NSR::PartitionMap &
NSR::PartMapTable::operator[]( UINT16 i )
{
    if (i < numEnts && entry != 0)
        return entry[i];
    else
        return nullEntry;
}

const NSR::PartitionMap &
NSR::PartMapTable::operator[]( UINT16 i ) const
{
    if (i < numEnts && entry != 0)
        return entry[i];
    else
        return nullEntry;
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::PartMapTable::operator != ( const PartMapTable & obj ) const
{
    UINT16 bad = 0;

    bad += ( numEnts != obj.numEnts );
    if (entry == 0 || obj.entry == 0)
        bad += 1000;
    else {
        for ( unsigned i = 0; i < numEnts; i++ ) {
            bad += ( entry[i] != obj.entry[i] );
        }
    }
    return bad != 0 ? True : False;
}
#endif

NSR::Error
NSR::PartMapTable::error() const
{
    if ( numEnts != 0 && entry == 0 ) {
        return Error(error_No_free_store);
    } else {
        return Error(error_None);
    }
}

void
NSR::PartMapTable::clear()
{
    if (entry != 0) {
        for ( unsigned i = 0; i < numEnts; i++ ) {
            entry[i].clear();
        }
    }
}

void
NSR::PartMapTable::fitEnts( UINT16 ne )
{
    if ( ne != numEnts ) {
        freeEnts();
        allocEnts( ne );
    } else {
        clear();
    }
}

void
NSR::PartMapTable::toISO( BYTE_PTR rbp )
{
    rbp += field.start();

    if (entry != 0) {
        for ( unsigned i = 0; i < numEnts; i++ ) {
            entry[i].toISO( rbp );
            rbp += entry->field.len();
        }
    }
}

void
NSR::PartMapTable::fromISO( CONST_BYTE_PTR rbp )
{
    rbp += field.start();

    if (entry != 0) {
        for ( unsigned i = 0; i < numEnts; i++ ) {
            entry[i].fromISO( rbp );
            rbp += entry->field.len();
        }
    }
}

// LogicalVolumeDesc

NSR::LogicalVolumeDesc::LogicalVolumeDesc( UINT32 n ) : 

    TaggedDescriptor( tag_LVD ), // len filled in later.
    volDescSeqNo       (  16 ),
    descCharSet        (  20 ),
    logicalVolumeID    (  84, 128 ),
    logicalBlockSize   ( 212 ),
    domainID           ( 216 ),
    fileSetDesc        ( 248 ),
    mapTableLength     ( 264 ),
    noPartMaps         ( 268 ),
    implementID        ( 272 ),
    implementUse       ( 304, 128 ),
    integritySeqExtent ( 432 ),
    partitionMapTable  ( 440, n )
{
    mapTableLength   = partitionMapTable.field.len();
    noPartMaps       = n;
    descLen( partitionMapTable.field.after() );

    bodyInit();
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::LogicalVolumeDesc::operator != ( const LogicalVolumeDesc & obj ) const
{
    UINT16 bad = 0;

    bad += ( volDescSeqNo       != obj.volDescSeqNo        );
    bad += ( descCharSet        != obj.descCharSet         );
    bad += ( logicalVolumeID    != obj.logicalVolumeID     );
    bad += ( logicalBlockSize   != obj.logicalBlockSize    );
    bad += ( domainID           != obj.domainID            );
    bad += ( fileSetDesc        != obj.fileSetDesc         );
    bad += ( mapTableLength     != obj.mapTableLength      );
    bad += ( noPartMaps         != obj.noPartMaps          );
    bad += ( implementID        != obj.implementID         );
    bad += ( implementUse       != obj.implementUse        );
    bad += ( integritySeqExtent != obj.integritySeqExtent  );
    bad += ( partitionMapTable  != obj.partitionMapTable   );

    return bad != 0 ? True : False;
}

NSR::Error
NSR::LogicalVolumeDesc::bodyVerifyISO( CONST_BYTE_PTR rbp )
{
    LogicalVolumeDesc tmp;
    tmp.bodyFromISO(rbp);
    if (tmp != *this) {
        return Error(error_VerifyLVD);
    } else {
        return Error(error_None);
    }
}
#endif

NSR::Error
NSR::LogicalVolumeDesc::bodyError() const
{
    Error err;

    if (( err = descCharSet        .error()) ) return err;
    if (( err = logicalVolumeID    .error()) ) return err;
    if (( err = domainID           .error()) ) return err;
    if (( err = implementID        .error()) ) return err;
    if (( err = implementUse       .error()) ) return err;
    if (( err = partitionMapTable  .error()) ) return err;
    return err;
}

void
NSR::LogicalVolumeDesc::bodyClear()
{
    volDescSeqNo       .clear();
    descCharSet        .clear();
    logicalVolumeID    .clear();
    logicalBlockSize   .clear();
    domainID           .clear();
    fileSetDesc        .clear();
    // mapTableLength    (not altered here)
    // noPartMaps        (not altered here)
    implementID        .clear();
    implementUse       .clear();
    integritySeqExtent .clear();
    partitionMapTable  .clear();
}

void
NSR::LogicalVolumeDesc::bodyInit()
{
    // logicalBlockSize   = 1024; // Better practice to leave as zero.
    implementID        .descVersionNo(descVersion_);
}

void
NSR::LogicalVolumeDesc::bodyToISO( BYTE_PTR rbp )
{
    volDescSeqNo       .toISO( rbp );
    descCharSet        .toISO( rbp );
    logicalVolumeID    .toISO( rbp );
    logicalBlockSize   .toISO( rbp );
    domainID           .toISO( rbp );
    fileSetDesc        .toISO( rbp );
    mapTableLength     .toISO( rbp );
    noPartMaps         .toISO( rbp );
    implementID        .toISO( rbp );
    implementUse       .toISO( rbp );
    integritySeqExtent .toISO( rbp );
    partitionMapTable  .toISO( rbp );
}

void
NSR::LogicalVolumeDesc::bodyFromISO( CONST_BYTE_PTR rbp )
{
    volDescSeqNo       .fromISO( rbp );
    descCharSet        .fromISO( rbp );
    logicalVolumeID    .fromISO( rbp, descCharSet.charSetType() );
    logicalBlockSize   .fromISO( rbp );
    domainID           .fromISO( rbp );
    fileSetDesc        .fromISO( rbp );
    mapTableLength     .fromISO( rbp );
    noPartMaps         .fromISO( rbp );
    implementID        .fromISO( rbp );
    implementUse       .fromISO( rbp );
    integritySeqExtent .fromISO( rbp );

    partitionMapTable  .fitEnts(noPartMaps);
    partitionMapTable  .fromISO( rbp );
}

// UnallocatedSpaceDesc

NSR::UnallocatedSpaceDesc::UnallocatedSpaceDesc() : 

    TaggedDescriptor( tag_USD, 512 ),
    volDescSeqNo  ( 16 ),
    numAllocDescs ( 20 ) {
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::UnallocatedSpaceDesc::operator != ( const UnallocatedSpaceDesc & obj ) const
{
    UINT16 bad = 0;

    bad += ( volDescSeqNo    != obj.volDescSeqNo    );
    bad += ( numAllocDescs   != obj.numAllocDescs   );

    return bad != 0 ? True : False;
}

NSR::Error
NSR::UnallocatedSpaceDesc::bodyVerifyISO( CONST_BYTE_PTR rbp )
{
    UnallocatedSpaceDesc tmp;
    tmp.bodyFromISO(rbp);
    if (tmp != *this) {
        return Error(error_VerifyUSD);
    } else {
        return Error(error_None);
    }
}
#endif

NSR::Error
NSR::UnallocatedSpaceDesc::bodyError() const
{
    return Error(error_None);
}

void
NSR::UnallocatedSpaceDesc::bodyClear()
{
    volDescSeqNo   .clear();
    numAllocDescs  .clear();
}

void
NSR::UnallocatedSpaceDesc::bodyToISO( BYTE_PTR rbp )
{
    volDescSeqNo   .toISO(rbp);
    numAllocDescs  .toISO(rbp);
}

void
NSR::UnallocatedSpaceDesc::bodyFromISO( CONST_BYTE_PTR rbp )
{
    volDescSeqNo   .fromISO(rbp);
    numAllocDescs  .fromISO(rbp);
}

// TerminatingDesc

NSR::TerminatingDesc::TerminatingDesc() : 

    TaggedDescriptor( tag_TD, 512 ),
    reserved ( 16, 496)
{}

// FileSetDesc

NSR::FileSetDesc::FileSetDesc() :

    TaggedDescriptor( tag_FSD, 512 ),
    recordDateTime         (  16 ),
    interchgLevel          (  28 ),
    maxInterchgLevel       (  30 ),
    charSetList            (  32 ),
    maxCharSetList         (  36 ),
    fileSetNo              (  40 ),
    fileSetDescNo          (  44 ),
    logicalVolumeIDCharSet (  48 ),
    logicalVolumeID        ( 112, 128 ),
    fileSetCharSet         ( 240 ),
    fileSetID              ( 304, 32 ),
    copyrightFileID        ( 336, 32 ),
    abstractFileID         ( 368, 32 ),
    rootDirICB             ( 400 ),
    domainID               ( 416 ),
    nextExtent             ( 448 ),
    reserved               ( 464, 48 )
{
    bodyInit();
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::FileSetDesc::operator != ( const FileSetDesc & obj ) const
{
    UINT16 bad = 0;

    bad += ( recordDateTime         != obj.recordDateTime          );
    bad += ( interchgLevel          != obj.interchgLevel           );
    bad += ( maxInterchgLevel       != obj.maxInterchgLevel        );
    bad += ( charSetList            != obj.charSetList             );
    bad += ( maxCharSetList         != obj.maxCharSetList          );
    bad += ( fileSetNo              != obj.fileSetNo               );
    bad += ( fileSetDescNo          != obj.fileSetDescNo           );
    bad += ( logicalVolumeIDCharSet != obj.logicalVolumeIDCharSet  );
    bad += ( logicalVolumeID        != obj.logicalVolumeID         );
    bad += ( fileSetCharSet         != obj.fileSetCharSet          );
    bad += ( fileSetID              != obj.fileSetID               );
    bad += ( copyrightFileID        != obj.copyrightFileID         );
    bad += ( abstractFileID         != obj.abstractFileID          );
    bad += ( rootDirICB             != obj.rootDirICB              );
    bad += ( domainID               != obj.domainID                );
    bad += ( nextExtent             != obj.nextExtent              );
    bad += ( reserved               != obj.reserved                );

    return bad != 0 ? True : False;
}

NSR::Error
NSR::FileSetDesc::bodyVerifyISO( CONST_BYTE_PTR rbp )
{
    FileSetDesc tmp;
    tmp.bodyFromISO(rbp);
    if (tmp != *this) {
        return Error(error_VerifyFSD);
    } else {
        return Error(error_None);
    }
}
#endif

NSR::Error
NSR::FileSetDesc::bodyError() const
{
    Error err;

    if (( err = logicalVolumeIDCharSet .error()) ) return err;
    if (( err = logicalVolumeID        .error()) ) return err;
    if (( err = fileSetCharSet         .error()) ) return err;
    if (( err = fileSetID              .error()) ) return err;
    if (( err = copyrightFileID        .error()) ) return err;
    if (( err = abstractFileID         .error()) ) return err;
    if (( err = domainID               .error()) ) return err;
    return err;
}

void
NSR::FileSetDesc::bodyClear()
{
    recordDateTime         .clear();
    interchgLevel          .clear();
    maxInterchgLevel       .clear();
    charSetList            .clear();
    maxCharSetList         .clear();
    fileSetNo              .clear();
    fileSetDescNo          .clear();
    logicalVolumeIDCharSet .clear();
    logicalVolumeID        .clear();
    fileSetCharSet         .clear();
    fileSetID              .clear();
    copyrightFileID        .clear();
    abstractFileID         .clear();
    rootDirICB             .clear();
    domainID               .clear();
    nextExtent             .clear();
    reserved               .clear();       
}

void
NSR::FileSetDesc::bodyInit()
{
    recordDateTime         = time(0);
    interchgLevel          = 3;          // OSTA 2.3.2.1
    maxInterchgLevel       = 3;          // OSTA 2.3.2.2
    charSetList            = 0x00000001; // OSTA 2.3.2.3
    maxCharSetList         = 0x00000001; // OSTA 2.3.2.4
}

void
NSR::FileSetDesc::bodyToISO( BYTE_PTR rbp )
{
    recordDateTime         .toISO( rbp );
    interchgLevel          .toISO( rbp );
    maxInterchgLevel       .toISO( rbp );
    charSetList            .toISO( rbp );
    maxCharSetList         .toISO( rbp );
    fileSetNo              .toISO( rbp );
    fileSetDescNo          .toISO( rbp );
    logicalVolumeIDCharSet .toISO( rbp );
    logicalVolumeID        .toISO( rbp );
    fileSetCharSet         .toISO( rbp );
    fileSetID              .toISO( rbp );
    copyrightFileID        .toISO( rbp );
    abstractFileID         .toISO( rbp );
    rootDirICB             .toISO( rbp );
    domainID               .toISO( rbp );
    nextExtent             .toISO( rbp );
    reserved               .toISO( rbp );       
}

void
NSR::FileSetDesc::bodyFromISO( CONST_BYTE_PTR rbp )
{
    recordDateTime         .fromISO( rbp );
    interchgLevel          .fromISO( rbp );
    maxInterchgLevel       .fromISO( rbp );
    charSetList            .fromISO( rbp );
    maxCharSetList         .fromISO( rbp );
    fileSetNo              .fromISO( rbp );
    fileSetDescNo          .fromISO( rbp );
    logicalVolumeIDCharSet .fromISO( rbp );
    logicalVolumeID        .fromISO( rbp, logicalVolumeIDCharSet.charSetType() );
    fileSetCharSet         .fromISO( rbp );
    fileSetID              .fromISO( rbp, fileSetCharSet.charSetType() );
    copyrightFileID        .fromISO( rbp, fileSetCharSet.charSetType() );
    abstractFileID         .fromISO( rbp, fileSetCharSet.charSetType() );
    rootDirICB             .fromISO( rbp );
    domainID               .fromISO( rbp );
    nextExtent             .fromISO( rbp );
    reserved               .fromISO( rbp );
}

// Uint32Table

void
NSR::Uint32Table::allocEnts( UINT16 ne )
{
    entry = new Uint32[ne?ne:1]; // Never allocate zero!
    numEnts = ne;
    if (entry != 0)
        field.len( ne * entry->field.len() );
    else
        field.len(0);
    clear();
}

NSR::Uint32Table::Uint32Table( UINT16 o, UINT16 ne ) :
    field( o, 0 )               // len filled in by allocEnts.
{
    allocEnts( ne );
}

NSR::Uint32Table::~Uint32Table() { freeEnts(); }

NSR::Uint32 &
NSR::Uint32Table::operator[]( UINT16 i)
{
    if (i < numEnts && entry != 0)
        return entry[i];
    else
        return nullEntry;
}

const NSR::Uint32 &
NSR::Uint32Table::operator[]( UINT16 i) const
{
    if (i < numEnts && entry != 0)
        return entry[i];
    else
        return nullEntry;
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::Uint32Table::operator != ( const Uint32Table & obj ) const
{
    UINT16 bad = 0;

    bad += ( numEnts != obj.numEnts );
    if (entry == 0 || obj.entry == 0)
        bad += 1000;
    else {
        for ( unsigned i = 0; i < numEnts; i++ ) {
            bad += ( entry[i] != obj.entry[i] );
        }
    }
    return bad != 0 ? True : False;
}
#endif

NSR::Error
NSR::Uint32Table::error() const
{
    if ( numEnts != 0 && entry == 0 ) {
        return Error(error_No_free_store);
    } else {
        return Error(error_None);
    }
}

void
NSR::Uint32Table::clear()
{
    if (entry != 0) {
        for ( unsigned i = 0; i < numEnts; i++ ) {
            entry[i].clear();
        }
    }
}

void
NSR::Uint32Table::fitEnts( UINT16 ne )
{
    if ( ne != numEnts ) {
        freeEnts();
        allocEnts( ne );
    } else {
        clear();
    }
}

void
NSR::Uint32Table::toISO( BYTE_PTR rbp )
{
    rbp += field.start();

    if (entry != 0) {
        for ( unsigned i = 0; i < numEnts; i++ ) {
            entry[i].toISO( rbp );
            rbp += entry->field.len();
        }
    }
}

void
NSR::Uint32Table::fromISO( CONST_BYTE_PTR rbp )
{
    rbp += field.start();

    if (entry != 0) {
        for ( unsigned i = 0; i < numEnts; i++ ) {
            entry[i].fromISO( rbp );
            rbp += entry->field.len();
        }
    }
}

// Int32Table

void
NSR::Int32Table::allocEnts( UINT16 ne )
{
    entry = new Int32[ne?ne:1]; // Never allocate zero!
    numEnts = ne;
    if (entry != 0)
        field.len( ne * entry->field.len() );
    else
        field.len(0);
    clear();
}

NSR::Int32Table::Int32Table( UINT16 o, UINT16 ne ) :
    field( o, 0 )               // len filled in by allocEnts.
{
    allocEnts( ne );
}

NSR::Int32Table::~Int32Table() { freeEnts(); }

NSR::Int32 &
NSR::Int32Table::operator[]( UINT16 i)
{
    if (i < numEnts && entry != 0)
        return entry[i];
    else
        return nullEntry;
}

const NSR::Int32 &
NSR::Int32Table::operator[]( UINT16 i) const
{
    if (i < numEnts && entry != 0)
        return entry[i];
    else
        return nullEntry;
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::Int32Table::operator != ( const Int32Table & obj ) const
{
    UINT16 bad = 0;

    bad += ( numEnts != obj.numEnts );
    if (entry == 0 || obj.entry == 0)
        bad += 1000;
    else {
        for ( unsigned i = 0; i < numEnts; i++ ) {
            bad += ( entry[i] != obj.entry[i] );
        }
    }
    return bad != 0 ? True : False;
}
#endif

NSR::Error
NSR::Int32Table::error() const
{
    if ( numEnts != 0 && entry == 0 ) {
        return Error(error_No_free_store);
    } else {
        return Error(error_None);
    }
}

void
NSR::Int32Table::clear()
{
    if (entry != 0) {
        for ( unsigned i = 0; i < numEnts; i++ ) {
            entry[i].clear();
        }
    }
}

void
NSR::Int32Table::fitEnts( UINT16 ne )
{
    if ( ne != numEnts ) {
        freeEnts();
        allocEnts( ne );
    } else {
        clear();
    }
}

void
NSR::Int32Table::toISO( BYTE_PTR rbp )
{
    rbp += field.start();

    if (entry != 0) {
        for ( unsigned i = 0; i < numEnts; i++ ) {
            entry[i].toISO( rbp );
            rbp += entry->field.len();
        }
    }
}

void
NSR::Int32Table::fromISO( CONST_BYTE_PTR rbp )
{
    rbp += field.start();

    if (entry != 0) {
        for ( unsigned i = 0; i < numEnts; i++ ) {
            entry[i].fromISO( rbp );
            rbp += entry->field.len();
        }
    }
}

// LogicalVolumeIntegrityDesc

NSR::LogicalVolumeIntegrityDesc::LogicalVolumeIntegrityDesc(
    UINT32 n,
    UINT32 licbt ) : 

    TaggedDescriptor( tag_LVID ), // length is calculated
    recordDateTime      (  16 ),
    integrityType_      (  28 ),
    nextIntegrityExtent (  32 ),
    uniqueID            (  40 ),
    reserved            (  48, 24 ),
    numPartitions       (  72 ),
    lenImplementUse     (  76 ),
    freeSpaceTable      (  80, n ),
    sizeTable           (   0, n ), // start floats
    implementID         (   0 ),    // start floats
    numFiles            (   0 ),    // start floats
    numDirectories      (   0 ),    // start floats
    icbTableLength      (   0 ),    // start floats
    icbTable            (   0, licbt, ad_Long ) // start floats
{
    numPartitions   = n;
    lenImplementUse = (implementID.field.len() +
                       numFiles.field.len() +
                       numDirectories.field.len());
    bodyInit();                 // Must be called before checking
                                // hasICBTable().
    if ( hasICBTable() ) {
        lenImplementUse += (icbTableLength.field.len() +
                            icbTable.field.len());
        icbTableLength  = icbTable.field.len(); // Should not this go
                                                // into Init() as
                                                // well?
    }
    affixFloatingFields();
    affixImplUseFields();

    if ( hasICBTable() )
        descLen( icbTable.field.after() );
    else
        descLen( numDirectories.field.after() );
}

void
NSR::LogicalVolumeIntegrityDesc::affixFloatingFields()
{
    sizeTable           .field.start( freeSpaceTable .field.after());
    implementID         .field.start( sizeTable      .field.after());
}

void
NSR::LogicalVolumeIntegrityDesc::affixImplUseFields()
{
    numFiles            .field.start( implementID    .field.after());
    numDirectories      .field.start( numFiles       .field.after());
    icbTableLength      .field.start( numDirectories .field.after());
    icbTable            .field.start( icbTableLength .field.after());
}

NSR::IntegrityType
NSR::LogicalVolumeIntegrityDesc::integrityType(void) const
{
    switch (integrityType_) {
    case integ_Open   : return integ_Open    ;
    case integ_Closed : return integ_Closed  ;
    default           : return integ_Unknown ;
    }
}

#ifdef NSR_VERIFY
NSR::Boolean
NSR::LogicalVolumeIntegrityDesc::operator != ( const
    LogicalVolumeIntegrityDesc & obj ) const
{
    UINT16 bad = 0;

    bad += ( recordDateTime      != obj.recordDateTime       );
    bad += ( integrityType_      != obj.integrityType_       );
    bad += ( nextIntegrityExtent != obj.nextIntegrityExtent  );
    bad += ( uniqueID            != obj.uniqueID             );
    bad += ( reserved            != obj.reserved             );
    bad += ( numPartitions       != obj.numPartitions        );
    bad += ( lenImplementUse     != obj.lenImplementUse      );
    bad += ( freeSpaceTable      != obj.freeSpaceTable       );
    bad += ( sizeTable           != obj.sizeTable            );
    bad += ( implementID         != obj.implementID          );
    bad += ( numFiles            != obj.numFiles             );
    bad += ( numDirectories      != obj.numDirectories       );
    if ( hasICBTable() ) {
        bad += ( icbTableLength  != obj.icbTableLength       );
        bad += ( icbTable        != obj.icbTable             );
    }

    return bad != 0 ? True : False;
}

NSR::Error
NSR::LogicalVolumeIntegrityDesc::bodyVerifyISO( CONST_BYTE_PTR rbp )
{
    LogicalVolumeIntegrityDesc tmp(0,0);
    tmp.bodyFromISO(rbp);
    if (tmp != *this) {
        return Error(error_VerifyLVID);
    } else {
        return Error(error_None);
    }
}
#endif

NSR::Error
NSR::LogicalVolumeIntegrityDesc::bodyError() const
{
    Error err;

    if (( err = freeSpaceTable .error()) ) return err;
    if (( err = sizeTable      .error()) ) return err;
    if (( err = implementID    .error()) ) return err;
    if ( hasICBTable() ) {
        if (( err = icbTable   .error()) ) return err;
    }
    return err;
}

void
NSR::LogicalVolumeIntegrityDesc::bodyClear()
{
    recordDateTime      .clear();
    integrityType_      .clear();
    nextIntegrityExtent .clear();
    uniqueID            .clear();
    reserved            .clear();
    // numPartitions       (not here)
    // lenImplementUse     (not here)
    freeSpaceTable      .clear();
    sizeTable           .clear();
    implementID         .clear();
    numFiles            .clear();
    numDirectories      .clear();
    // icbTableLength     (not here)
    icbTable            .clear();
}

void
NSR::LogicalVolumeIntegrityDesc::bodyInit()
{
    recordDateTime = time(0);
    integrityType_ = integ_Open;
    implementID    .descVersionNo(descVersion_);
}

void
NSR::LogicalVolumeIntegrityDesc::bodyToISO( BYTE_PTR rbp )
{
    recordDateTime      .toISO( rbp );
    integrityType_      .toISO( rbp );
    nextIntegrityExtent .toISO( rbp );
    uniqueID            .toISO( rbp );
    reserved            .toISO( rbp );
    numPartitions       .toISO( rbp );
    lenImplementUse     .toISO( rbp );
    freeSpaceTable      .toISO( rbp );
    sizeTable           .toISO( rbp );
    implementID         .toISO( rbp );
    numFiles            .toISO( rbp );
    numDirectories      .toISO( rbp );
    if ( hasICBTable() ) {
        icbTableLength  .toISO( rbp );
        icbTable        .toISO( rbp );
    }
}

void
NSR::LogicalVolumeIntegrityDesc::bodyFromISO( CONST_BYTE_PTR rbp )
{
    recordDateTime      .fromISO( rbp );
    integrityType_      .fromISO( rbp );
    nextIntegrityExtent .fromISO( rbp );
    uniqueID            .fromISO( rbp );
    reserved            .fromISO( rbp );
    numPartitions       .fromISO( rbp );
    lenImplementUse     .fromISO( rbp );

    freeSpaceTable      .fitEnts( numPartitions );
    sizeTable           .fitEnts( numPartitions );

    affixFloatingFields();

    freeSpaceTable      .fromISO( rbp );
    sizeTable           .fromISO( rbp );
    implementID         .fromISO( rbp );

    affixImplUseFields();

    numFiles            .fromISO( rbp );
    numDirectories      .fromISO( rbp );

    if ( ( implementID.id() == NSR::ImplRegID::HPEarlyID ) ||
         ( implementID.id() == NSR::ImplRegID::HPID ) ) {
        // This means that HP wrote it, and it thus supports
        // descriptor versions, which hasICBTable() is based upon.
        if ( hasICBTable() ) {
            icbTableLength      .fromISO( rbp );
            icbTable            .fitToLen( icbTableLength );
            icbTable            .fromISO( rbp );
        }
    } else {
        icbTableLength      .clear();
        icbTable            .fitToLen( 1 );
        icbTable            .clear();
    }

// CLEANUP: When icbTables are changed to support a length of zero,
// only icbTableLength needs to be zeroed out if the implementIDs are
// different. The other lines (icbTable.fitToLen(),
// icbTable.fromISO(), and associatedFileSetNo.fromISO()) may be moved
// to the end of the function, and called in both cases.

}
