#include "precomp.h"


//
// OD2.CPP
// Order Decoding Second Level
//
// Copyright(c) Microsoft 1997-
//

#define MLZ_FILE_ZONE  ZONE_ORDER



//
// OD2_ViewStarting()
//
// For 3.0 nodes, we create the decoding data each time they start hosting.
// For 2.x nodes, we create the decoding data once and use it until they
//      leave the share.
//
BOOL  ASShare::OD2_ViewStarting(ASPerson * pasPerson)
{
    PPARTYORDERDATA     pThisParty;
    BOOL                rc = FALSE;

    DebugEntry(ASShare::OD2_ViewStarting);

    ValidatePerson(pasPerson);

    //
    // Allocate memory for the required structure.
    //
    pThisParty = new PARTYORDERDATA;
    pasPerson->od2Party = pThisParty;
    if (!pThisParty)
    {
        ERROR_OUT(( "Failed to get memory for od2Party entry"));
        DC_QUIT;
    }

    //
    // Ensure the pointers are correctly set up.
    //
    ZeroMemory(pThisParty, sizeof(*pThisParty));
    SET_STAMP(pThisParty, PARTYORDERDATA);

    pThisParty->LastOrder[OE2_DSTBLT_ORDER    ] = &pThisParty->LastDstblt;
    pThisParty->LastOrder[OE2_PATBLT_ORDER    ] = &pThisParty->LastPatblt;
    pThisParty->LastOrder[OE2_SCRBLT_ORDER    ] = &pThisParty->LastScrblt;
    pThisParty->LastOrder[OE2_MEMBLT_ORDER    ] = &pThisParty->LastMemblt;
    pThisParty->LastOrder[OE2_MEM3BLT_ORDER   ] = &pThisParty->LastMem3blt;
    pThisParty->LastOrder[OE2_TEXTOUT_ORDER   ] = &pThisParty->LastTextOut;
    pThisParty->LastOrder[OE2_EXTTEXTOUT_ORDER] = &pThisParty->LastExtTextOut;
    pThisParty->LastOrder[OE2_RECTANGLE_ORDER ] = &pThisParty->LastRectangle;
    pThisParty->LastOrder[OE2_LINETO_ORDER    ] = &pThisParty->LastLineTo;
    pThisParty->LastOrder[OE2_OPAQUERECT_ORDER] = &pThisParty->LastOpaqueRect;
    pThisParty->LastOrder[OE2_SAVEBITMAP_ORDER] = &pThisParty->LastSaveBitmap;
    pThisParty->LastOrder[OE2_DESKSCROLL_ORDER] = &pThisParty->LastDeskScroll;
    pThisParty->LastOrder[OE2_MEMBLT_R2_ORDER ] = &pThisParty->LastMembltR2;
    pThisParty->LastOrder[OE2_MEM3BLT_R2_ORDER] = &pThisParty->LastMem3bltR2;
    pThisParty->LastOrder[OE2_POLYGON_ORDER   ] = &pThisParty->LastPolygon;
    pThisParty->LastOrder[OE2_PIE_ORDER       ] = &pThisParty->LastPie;
    pThisParty->LastOrder[OE2_ELLIPSE_ORDER   ] = &pThisParty->LastEllipse;
    pThisParty->LastOrder[OE2_ARC_ORDER       ] = &pThisParty->LastArc;
    pThisParty->LastOrder[OE2_CHORD_ORDER     ] = &pThisParty->LastChord;
    pThisParty->LastOrder[OE2_POLYBEZIER_ORDER] = &pThisParty->LastPolyBezier;
    pThisParty->LastOrder[OE2_ROUNDRECT_ORDER]  = &pThisParty->LastRoundRect;

    OD2_SyncIncoming(pasPerson);

    rc = TRUE;

DC_EXIT_POINT:
    DebugExitBOOL(ASShare::OD2_ViewStarting, rc);
    return(rc);
}



//
// OD2_SyncIncoming()
// Called when NEW dude starts to share, a share is created, or someone new
// joins the share.
//
void ASShare::OD2_SyncIncoming(ASPerson * pasPerson)
{
    PPARTYORDERDATA     pThisParty;

    DebugEntry(ASShare::OD2_SyncIncoming);

    ValidateView(pasPerson);

    pThisParty = pasPerson->od2Party;

    pThisParty->LastOrderType = OE2_PATBLT_ORDER;
    pThisParty->pLastOrder    =
               (LPCOM_ORDER)(pThisParty->LastOrder[pThisParty->LastOrderType]);

    //
    // Set all buffers to NULL Fill in the datalength fields and the type
    // field.  Note that because the type field is always the first one in
    // an order we can cast each pointer to a TEXTOUT order to get the
    // correct position for this field
    //
#define Reset(field, ord)                                               \
{                                                                       \
    ZeroMemory(&pThisParty->field, sizeof(pThisParty->field));             \
    ((LPCOM_ORDER_HEADER)pThisParty->field)->cbOrderDataLength =          \
           sizeof(pThisParty->field) - sizeof(COM_ORDER_HEADER);         \
    TEXTFIELD(((LPCOM_ORDER)pThisParty->field))->type = LOWORD(ord);      \
}

    //
    // The compiler generates a warning for our use of LOWORD here on a
    // constant.  We disable the warning just for now.
    //

    Reset(LastDstblt,     ORD_DSTBLT);
    Reset(LastPatblt,     ORD_PATBLT);
    Reset(LastScrblt,     ORD_SCRBLT);
    Reset(LastMemblt,     ORD_MEMBLT);
    Reset(LastMem3blt,    ORD_MEM3BLT);
    Reset(LastTextOut,    ORD_TEXTOUT);
    Reset(LastExtTextOut, ORD_EXTTEXTOUT);
    Reset(LastRectangle,  ORD_RECTANGLE);
    Reset(LastLineTo,     ORD_LINETO);
    Reset(LastOpaqueRect, ORD_OPAQUERECT);
    Reset(LastSaveBitmap, ORD_SAVEBITMAP);
    Reset(LastDeskScroll, ORD_DESKSCROLL);
    Reset(LastMembltR2,   ORD_MEMBLT_R2);
    Reset(LastMem3bltR2,  ORD_MEM3BLT_R2);
    Reset(LastPolygon,    ORD_POLYGON);
    Reset(LastPie,        ORD_PIE);
    Reset(LastEllipse,    ORD_ELLIPSE);
    Reset(LastArc,        ORD_ARC);
    Reset(LastChord,      ORD_CHORD);
    Reset(LastPolyBezier, ORD_POLYBEZIER);
    Reset(LastRoundRect,  ORD_ROUNDRECT);

    //
    // Reset the bounds rectangle
    //
    ZeroMemory(&pThisParty->LastBounds, sizeof(pThisParty->LastBounds));

    //
    // The sender and the receiver both set their structures to the same
    // NULL state and the sender only ever sends differences from the
    // current state.  However the fontID fields in the received orders
    // refer to the sender, so we must actually set our fontID fields to
    // the local equivalent of the NULL entries just set.
    // We cannot do this until we have actually received the font details
    // so set the field to a dummy value we can recognise later.
    //
    TEXTFIELD(((LPCOM_ORDER)pThisParty->LastTextOut))->common.FontIndex =
                                                                DUMMY_FONT_ID;
    EXTTEXTFIELD(((LPCOM_ORDER)pThisParty->LastExtTextOut))->common.
                                                   FontIndex = DUMMY_FONT_ID;

    DebugExitVOID(ASShare::OD2_SyncIncoming);
}



//
// OD2_ViewEnded()
//
void  ASShare::OD2_ViewEnded(ASPerson * pasPerson)
{
    DebugEntry(ASShare::OD2_ViewEnded);

    ValidatePerson(pasPerson);

    //
    // For 3.0 nodes, we can free the decode data; 3.0 senders clear theirs
    //      every time they host.
    // For 2.x nodes, we must keep it around while they are in the share.
    //

    OD2FreeIncoming(pasPerson);

    DebugExitVOID(ASShare::OD2_ViewEnded);
}



//
// OD2_PartyLeftShare()
// For 2.x nodes, frees the incoming OD2 data
//
void ASShare::OD2_PartyLeftShare(ASPerson * pasPerson)
{
    DebugEntry(ASShare::OD2_PartyLeftShare);

    ValidatePerson(pasPerson);

    DebugExitVOID(ASShare::OD2_PartyLeftShare);
}


//
// OD2FreeIncoming()
// Frees per-party incoming OD2 resources
//
void ASShare::OD2FreeIncoming(ASPerson * pasPerson)
{
    DebugEntry(OD2FreeIncoming);

    if (pasPerson->od2Party != NULL)
    {
        if (pasPerson->od2Party->LastHFONT != NULL)
        {
            if (pasPerson->m_pView)
            {
                // For 3.0 nodes, pView won't be NULL; for 2.x nodes it may.

                //
                // This font might be currently selected into the DC for
                // this person's desktop.  Select it out.
                //
                SelectFont(pasPerson->m_pView->m_usrDC, (HFONT)GetStockObject(SYSTEM_FONT));
            }

            DeleteFont(pasPerson->od2Party->LastHFONT);
            pasPerson->od2Party->LastHFONT = NULL;
        }

        delete pasPerson->od2Party;
        pasPerson->od2Party = NULL;
    }

    DebugExitVOID(ASShare::OD2FreeIncoming);
}

//
// OD2_DecodeOrder()
//
LPCOM_ORDER  ASShare::OD2_DecodeOrder
(
    void *      pEOrder,
    LPUINT      pLengthDecoded,
    ASPerson *  pasPerson
)
{
    POE2ETFIELD       pTableEntry;
    UINT          FieldChangedBits;
    UINT          FieldsChanged;
    LPBYTE          pNextDataToCopy;
    RECT            Rect;
    LPBYTE          pControlFlags;
    LPTSHR_UINT32_UA      pEncodingFlags;
    LPSTR           pEncodedOrder;
    UINT            numEncodingFlagBytes;
    UINT            encodedFieldLength;
    UINT            unencodedFieldLength;
    UINT            numReps;
    UINT            i;
    LPBYTE          pDest;

    DebugEntry(ASShare::OD2_DecodeOrder);

    ValidatePerson(pasPerson);

    //
    // Set up some local variables to access the encoding buffer in various
    // ways.
    //
    pControlFlags  = &((PDCEO2ORDER)pEOrder)->ControlFlags;
    pEncodedOrder  = (LPSTR)&((PDCEO2ORDER)pEOrder)->EncodedOrder[0];
    pEncodingFlags = (LPTSHR_UINT32_UA)pEncodedOrder;

    if ( (*pControlFlags & OE2_CF_STANDARD_ENC) == 0)
    {
        ERROR_OUT(("Specially encoded order received from %d", pasPerson));
        return(NULL);
    }

    //
    // If the unencoded flag is set, the order has not been encoded, so
    // just return a pointer to the start of the data.
    //
    if ( (*pControlFlags & OE2_CF_UNENCODED) != 0)
    {
        //
        // Convert the fields of the order header from wire format.  Note
        // that unencoded orders are also PRIVATE, and hence do not
        // actually have the rcsDst field.
        //
        *pLengthDecoded = sizeof(COM_ORDER_HEADER)
          + EXTRACT_TSHR_UINT16_UA(
             &(((LPCOM_ORDER_UA)pEncodedOrder)->OrderHeader.cbOrderDataLength))
                      + FIELD_OFFSET(DCEO2ORDER, EncodedOrder);
        TRACE_OUT(("Person [%d] Returning unencoded buffer length %u",
                pasPerson->mcsID, *pLengthDecoded));
        return((LPCOM_ORDER)pEncodedOrder);
    }

    //
    // If type has changed, new type will be first byte in encoded order.
    // Get pointer to last order of this type. The encoding flags follow
    // this byte (if it is present).
    //
    if ( (*pControlFlags & OE2_CF_TYPE_CHANGE) != 0)
    {
        TRACE_OUT(("Person [%d] change type from %d to %d", pasPerson->mcsID,
                   (UINT)pasPerson->od2Party->LastOrderType,
                   (UINT)*(LPBYTE)pEncodedOrder));
        pasPerson->od2Party->LastOrderType = *(LPTSHR_UINT8)pEncodedOrder;
        pasPerson->od2Party->pLastOrder =
              (LPCOM_ORDER)(pasPerson->od2Party->LastOrder[pasPerson->od2Party->LastOrderType]);
        pEncodingFlags = (LPTSHR_UINT32_UA)&pEncodedOrder[1];
    }
    else
    {
        pEncodingFlags = (LPTSHR_UINT32_UA)&pEncodedOrder[0];
    }

    TRACE_OUT(("Person [%d] type %x", pasPerson->mcsID, pasPerson->od2Party->LastOrderType));

    //
    // Work out how many bytes we will need to store the encoding flags in.
    // (We have a flag for each field in the order structure). This code
    // we have written will cope with up to a DWORD of encoding flags.
    //
    numEncodingFlagBytes = (s_etable.NumFields[pasPerson->od2Party->LastOrderType]+7)/8;
    if (numEncodingFlagBytes > 4)
    {
        ERROR_OUT(( "[%#lx] Too many flag bytes (%d) for this code",
                   pasPerson, numEncodingFlagBytes));
    }

    //
    // Now we know how many bytes make up the flags we can get a pointer
    // to the position at which to start encoding the orders fields into.
    //
    pNextDataToCopy = (LPBYTE)pEncodingFlags + numEncodingFlagBytes;

    //
    // Reset the flags field to zero
    //
    pasPerson->od2Party->pLastOrder->OrderHeader.fOrderFlags = 0;

    //
    // Rebuild the Order Common Header in the same order as it was
    // encoded:
    //
    //
    // If a bounding rectangle is included, copy it into the order header
    //
    if ( *pControlFlags & OE2_CF_BOUNDS )
    {
        OD2DecodeBounds((LPTSHR_UINT8*)&pNextDataToCopy,
                        &pasPerson->od2Party->pLastOrder->OrderHeader.rcsDst,
                        pasPerson);
    }

    //
    // locate entry in encoding table for this ORDER type and extract the
    // encoded order flags from the Encoded order
    //
    pTableEntry      = s_etable.pFields[pasPerson->od2Party->LastOrderType];
    FieldChangedBits = 0;
    for (i=numEncodingFlagBytes; i>0; i--)
    {
        FieldChangedBits  = FieldChangedBits << 8;
        FieldChangedBits |= (UINT)((LPBYTE)pEncodingFlags)[i-1];
    }

    //
    // We need to keep a record of which fields we change.
    //
    FieldsChanged = FieldChangedBits;

    //
    // Now decode the order: While field changed bits are non-zero
    //   If rightmost bit is non-zero
    //       copy data from the buffer to the copy of this order type
    //   skip to next entry in Encoding table
    //   shift field changed bits right one bit
    //
    while (FieldChangedBits != 0)
    {
        //
        // If this field was encoded (ie changed since the last order)...
        //
        if ((FieldChangedBits & 1) != 0)
        {
            //
            // Set up a pointer to the destination (unencoded) field.
            //
            pDest = ((LPBYTE)pasPerson->od2Party->pLastOrder)
                  + pTableEntry->FieldPos
                  + sizeof(COM_ORDER_HEADER);

            //
            // If the field type is OE2_ETF_DATA, we just copy the number
            // of bytes given by the encoded length in the table.
            //
            if ((pTableEntry->FieldType & OE2_ETF_DATA) != 0)
            {
                encodedFieldLength   = 1;
                unencodedFieldLength = 1;
                numReps              = pTableEntry->FieldEncodedLen;

                TRACE_OUT(("Byte data field, len %d", numReps));
            }
            else
            {
                //
                // This is not a straightforward data copy.  The length of
                // the source and destination data is given in the table in
                // the FieldEncodedLen and FieldUnencodedLen elements
                // respectively.
                //
                encodedFieldLength   = pTableEntry->FieldEncodedLen;
                unencodedFieldLength = pTableEntry->FieldUnencodedLen;

                if ((pTableEntry->FieldType & OE2_ETF_FIXED) != 0)
                {
                    //
                    // If the field type is fixed (OE2_ETF_FIXED is set),
                    // we just have to decode one element of the given
                    // size.
                    //
                    numReps = 1;
                    TRACE_OUT(("Fixed fld: encoded size %d, unencoded size %d",
                             encodedFieldLength,
                             unencodedFieldLength));
                }
                else
                {
                    //
                    // This is a variable field.  The next byte to be
                    // decoded contains the number of BYTES of encoded data
                    // (not elements), so divide by the encoded field size
                    // to get numReps.
                    //
                    numReps = *pNextDataToCopy / encodedFieldLength;
                    TRACE_OUT(("Var field: encoded size %d, unencoded size " \
                                 "%d, reps %d",
                             encodedFieldLength,
                             unencodedFieldLength,
                             numReps));

                    //
                    // Step past the length field in the encoded order
                    //
                    pNextDataToCopy++;

                    //
                    // For a variable length field, the unencoded version
                    // contains a UINT for the length (in bytes) of the
                    // following variable data, followed by the actual
                    // data.  Fill in the length field in the unencoded
                    // order.
                    //
                    *(LPTSHR_UINT32)pDest = numReps * unencodedFieldLength;
                    pDest += sizeof(TSHR_UINT32);
                }
            }

            //
            // If the order was encoded using delta coordinate mode and
            // this field is a coordinate then convert the coordinate from
            // the single byte sized delta to a value of the size given by
            // unencodedFieldLen...
            //
            // Note that we've already handled the leading length field of
            // variable length fields above, so we don't have to worry
            // about FIXED / VARIABLE issues here.
            //
            if ( (*pControlFlags & OE2_CF_DELTACOORDS) &&
                 (pTableEntry->FieldType & OE2_ETF_COORDINATES) )
            {
                //
                // NOTE:
                // numReps can be zero in the case of an EXTTEXTOUT
                // order that needs the opaque rect but has no absolute
                // char positioning
                //
                OD2CopyFromDeltaCoords((LPTSHR_INT8*)&pNextDataToCopy,
                                       pDest,
                                       unencodedFieldLength,
                                       pTableEntry->FieldSigned,
                                       numReps);
            }
            else
            {
                if ((pasPerson->od2Party->LastOrderType == OE2_POLYGON_ORDER) ||
                    (pasPerson->od2Party->LastOrderType == OE2_POLYBEZIER_ORDER))
                {
                    //
                    // numReps can never be zero in this case
                    //
                    ASSERT(numReps);
                }
                OD2DecodeField(&pNextDataToCopy,
                               pDest,
                               encodedFieldLength,
                               unencodedFieldLength,
                               pTableEntry->FieldSigned,
                               numReps);
            }
        }

        //
        // Move on to the next field in the order structure...
        //
        FieldChangedBits = FieldChangedBits >> 1;
        pTableEntry++;
    }

    //
    // Check to see if we just got a font handle.
    // Because of the rather nasty test against an unnamed bit in the
    // FieldsChanged bits, we have a compile time check against the number
    // of fields in the TEXT orders structures.
    // The requirement for this code not to break is that the font handle
    // field must stay as the 13th field (hence 1 << 12).
    //

#if (OE2_NUM_TEXTOUT_FIELDS != 15) || (OE2_NUM_EXTTEXTOUT_FIELDS != 22)
#error code breaks if font handle not 13th field
#endif // OE2_NUM_TEXTOUT_FIELDS is 15 or 22

    if (((pasPerson->od2Party->LastOrderType == OE2_EXTTEXTOUT_ORDER) &&
         ((FieldsChanged & (1 << 12)) ||
          (EXTTEXTFIELD(((LPCOM_ORDER)pasPerson->od2Party->LastExtTextOut))->common.
                                             FontIndex == DUMMY_FONT_ID))) ||
        ((pasPerson->od2Party->LastOrderType == OE2_TEXTOUT_ORDER) &&
         ((FieldsChanged & (1 << 12)) ||
          (TEXTFIELD(((LPCOM_ORDER)pasPerson->od2Party->LastTextOut))->common.
                                             FontIndex == DUMMY_FONT_ID))))
    {
        //
        // This was a text order, and the font changed for it.
        //
        FH_ConvertAnyFontIDToLocal(pasPerson->od2Party->pLastOrder, pasPerson);
    }

    //
    // if the OE2_CF_BOUNDS flag is not set, we have not yet constructed
    // the bounding rectangle, so call OD2ReconstructBounds to do so
    //
    if ( (*pControlFlags & OE2_CF_BOUNDS) == 0)
    {
        OD2_CalculateBounds(pasPerson->od2Party->pLastOrder,
                           &Rect,
                           TRUE,
                           pasPerson);
        pasPerson->od2Party->pLastOrder->OrderHeader.rcsDst.left
                                                      = (TSHR_INT16)Rect.left;
        pasPerson->od2Party->pLastOrder->OrderHeader.rcsDst.right
                                                      = (TSHR_INT16)Rect.right;
        pasPerson->od2Party->pLastOrder->OrderHeader.rcsDst.top
                                                      = (TSHR_INT16)Rect.top;
        pasPerson->od2Party->pLastOrder->OrderHeader.rcsDst.bottom
                                                      = (TSHR_INT16)Rect.bottom;
        pasPerson->od2Party->pLastOrder->OrderHeader.fOrderFlags |= OF_NOTCLIPPED;
    }

    //
    // Return the decoded order length and a pointer to the order.
    //
    *pLengthDecoded = (UINT)(pNextDataToCopy - (LPBYTE)pEOrder);

    TRACE_OUT(("Person [%d] Return decoded order length %u",
               pasPerson->mcsID, *pLengthDecoded));

    DebugExitPVOID(ASShare::OD2_DecodeOrder, pasPerson->od2Party->pLastOrder);
    return(pasPerson->od2Party->pLastOrder);
}


//
// FUNCTION: OD2UseFont
//
// DESCRIPTION:
//
// Selects the font described by the parameters into the person's DC.
// so that we can then query the text extent etc.
// The queried metrics are available from pasPerson->od2Party->LastFontMetrics.
//
// PARAMETERS:
//
// RETURNS: TRUE if successful, FALSE otherwise.
//
//
BOOL  ASShare::OD2UseFont
(
    ASPerson *      pasPerson,
    LPSTR           pName,
    UINT            facelength,
    UINT            codePage,
    UINT            MaxHeight,
    UINT            Height,
    UINT            Width,
    UINT            Weight,
    UINT            flags
)
{
    BOOL          rc = TRUE;

    DebugEntry(ASShare::OD2UseFont);

    ValidatePerson(pasPerson);

    if ((pasPerson->od2Party->LastFontFaceLen != facelength                      ) ||
        (memcmp((LPSTR)(pasPerson->od2Party->LastFaceName),pName,
                                                facelength)   != 0      ) ||
        (pasPerson->od2Party->LastCodePage                             != codePage) ||
        (pasPerson->od2Party->LastFontHeight                           != Height ) ||
        (pasPerson->od2Party->LastFontWidth                            != Width  ) ||
        (pasPerson->od2Party->LastFontWeight                           != Weight ) ||
        (pasPerson->od2Party->LastFontFlags                            != flags  ))
    {
        TRACE_OUT(("Person [%d] Font %s (CP%d,w%d,h%d,f%04X,wgt%d) to %s (CP%d,w%d,h%d,f%04X,wgt%d)",
            pasPerson->mcsID, pasPerson->od2Party->LastFaceName,
            pasPerson->od2Party->LastCodePage, pasPerson->od2Party->LastFontWidth,
            pasPerson->od2Party->LastFontHeight,
                                                   pasPerson->od2Party->LastFontFlags,
                                                   pasPerson->od2Party->LastFontWeight,
                                                   pName,
                                                   codePage,
                                                   Width,
                                                   Height,
                                                   flags,
                                                   Weight ));

        memcpy(pasPerson->od2Party->LastFaceName,pName,facelength);
        pasPerson->od2Party->LastFontFaceLen            = facelength;
        pasPerson->od2Party->LastFaceName[facelength]   = '\0';
        pasPerson->od2Party->LastFontHeight             = Height;
        pasPerson->od2Party->LastCodePage                = codePage;
        pasPerson->od2Party->LastFontWidth              = Width;
        pasPerson->od2Party->LastFontWeight             = Weight;
        pasPerson->od2Party->LastFontFlags              = flags;

        rc = USR_UseFont(pasPerson->m_pView->m_usrDC,
                         &pasPerson->od2Party->LastHFONT,
                         &pasPerson->od2Party->LastFontMetrics,
                         (LPSTR)pasPerson->od2Party->LastFaceName,
                         codePage,
                         MaxHeight,
                         Height,
                         Width,
                         Weight,
                         flags);
    }
    else
    {
        //
        // The font hasn't changed, so LastHFONT should be the one we
        // want.  We must still select it in however, since several fonts
        // get selected into usrDC.
        //
        ASSERT(pasPerson->od2Party->LastHFONT != NULL);
        SelectFont(pasPerson->m_pView->m_usrDC, pasPerson->od2Party->LastHFONT);
    }

    DebugExitBOOL(ASShare::OD2UseFont, rc);
    return(rc);
}




//
// OD2_CalculateTextOutBounds()
//
void  ASShare::OD2_CalculateTextOutBounds
(
    LPTEXTOUT_ORDER pTextOut,
    LPRECT          pRect,
    BOOL            fDecoding,
    ASPerson *      pasPerson
)
{
    LPSTR            pString;
    int              cbString;
    BOOL             fExtTextOut;
    LPEXTTEXTOUT_ORDER pExtTextOut = NULL;
    LPCOMMON_TEXTORDER  pCommon;
    LPSTR            faceName;
    UINT             faceNameLength;
    BOOL             fFontSelected;
    UINT           FontIndex;
    UINT             width;
    UINT             maxFontHeight;
    UINT           nFontFlags;
    UINT           nCodePage;

    DebugEntry(ASShare::OD2_CalculateTextOutBounds);

    ValidatePerson(pasPerson);

    //
    // Workout if this is a TextOut or ExtTextOut order.
    //
    if (pTextOut->type == ORD_EXTTEXTOUT_TYPE)
    {
        fExtTextOut = TRUE;
        pExtTextOut = (LPEXTTEXTOUT_ORDER)pTextOut;
        pCommon     = &(pExtTextOut->common);

        //
        // This code does not cope with calculating the bounds of an
        // ExtTextOut order with a delta X array.  We return a NULL
        // rectangle in this case to force the OE2 code to transmit the
        // bounds explicitly.  However if we are decoding then we must
        // calculate the rectangle (even though it may be wrong) to
        // maintain backward compatability to previous versions of the
        // product (R11) which did not return a NULL rect if delta-x was
        // present.
        //
        if (  (pExtTextOut->fuOptions & ETO_LPDX)
           && (!fDecoding) )
        {
            TRACE_OUT(( "Delta X so return NULL rect"));
            pRect->left = 0;
            pRect->right = 0;
            pRect->top = 0;
            pRect->bottom = 0;
            return;
        }
    }
    else if (pTextOut->type == ORD_TEXTOUT_TYPE)
    {
        fExtTextOut = FALSE;
        pCommon     = &(pTextOut->common);
    }
    else
    {
        ERROR_OUT(( "{%p} Unexpected order type %x",
                    pasPerson, (int)pTextOut->type));
        return;
    }

    //
    // The order structures both have the variableString as their first
    // variable field. If this were not the case then the code here would
    // have to take into account that the encoding side packs variable
    // sized fields while the decoding side does not pack them.
    //
    if (fExtTextOut)
    {
        cbString   = pExtTextOut->variableString.len;
        pString    = (LPSTR)&pExtTextOut->variableString.string;
    }
    else
    {
        cbString   = pTextOut->variableString.len;
        pString    = (LPSTR)&pTextOut->variableString.string;
    }
    FontIndex = pCommon->FontIndex;
    width      = pCommon->FontWidth;

    //
    // Get the facename from the handle, and get the various font width/
    // height adjusted values.
    //
    faceName      = FH_GetFaceNameFromLocalHandle(FontIndex,
                                                  &faceNameLength);
    maxFontHeight = (UINT)FH_GetMaxHeightFromLocalHandle(FontIndex);

    //
    // Get the local font flags for the font, so that we can merge in any
    // specific local flag information when setting up the font.  The prime
    // example of this is whether the local font we matched is TrueType or
    // not, which information is not sent over the wire, but does need to
    // be used when setting up the font - or else we may draw using a local
    // fixed font of the same facename.
    //
    nFontFlags = FH_GetFontFlagsFromLocalHandle(FontIndex);

    //
    // Get the local codePage for the font.
    //
    nCodePage = FH_GetCodePageFromLocalHandle(FontIndex);

    //
    // Hosting only version does not ever decode orders.
    //

    //
    // Select the font into the appropriate DC and query the text extent.
    //
    if (fDecoding)
    {
        fFontSelected = OD2UseFont(pasPerson,
                                    faceName,
                                   faceNameLength,
                                   nCodePage,
                                   maxFontHeight,
                                   pCommon->FontHeight,
                                   width,
                                   pCommon->FontWeight,
                                   pCommon->FontFlags
                                                    | (nFontFlags & NF_LOCAL));
        if (!fFontSelected)
        {
            //
            // We failed to select the correct font - so we cannot
            // calculate the bounds correctly.  However, the fact that we
            // are in this routine means that on the host the text was
            // unclipped.  Therefore we just return a (fairly arbitrary)
            // very big rect.
            //
            // This is far from a perfect answer (for example, it will
            // force a big repaint), but allow us to keep running in a
            // difficult situation (i.e. acute resource shortage).
            //
            pRect->left = 0;
            pRect->right = 2000;
            pRect->top = -2000;
            pRect->bottom = 2000;
            return;
        }

        OE_GetStringExtent(pasPerson->m_pView->m_usrDC,
                            &pasPerson->od2Party->LastFontMetrics,
                            pString, cbString, pRect );
    }
    else
    {
        ASSERT(m_pHost);

        fFontSelected = m_pHost->OE2_UseFont(faceName,
                                   (TSHR_UINT16)faceNameLength,
                                   (TSHR_UINT16)nCodePage,
                                   (TSHR_UINT16)maxFontHeight,
                                   (TSHR_UINT16)pCommon->FontHeight,
                                   (TSHR_UINT16)width,
                                   (TSHR_UINT16)pCommon->FontWeight,
                                   (TSHR_UINT16)(pCommon->FontFlags
                                                  | (nFontFlags & NF_LOCAL)));

        if (!fFontSelected)
        {
            //
            // We failed to select the correct font. We return a NULL
            // rectangle in this case to force the OE2 code to transmit
            // the bounds explicitly.
            //
            pRect->left = 0;
            pRect->right = 0;
            pRect->top = 0;
            pRect->bottom = 0;
            return;
        }

        OE_GetStringExtent(m_pHost->m_usrWorkDC, NULL, pString, cbString, pRect );
    }

    //
    // We have a rectangle with the text extent in it relative to (0,0) so
    // add in the text starting position to this to give us the bounding
    // rectangle. At the same time we will convert the exclusive rect
    // returned by OE_GetStringExtent to an inclusive rectangle as us
    //
    pRect->left   += pCommon->nXStart;
    pRect->right  += pCommon->nXStart - 1;
    pRect->top    += pCommon->nYStart;
    pRect->bottom += pCommon->nYStart - 1;

    //
    // If this is an ExtTextOut order then we must take into account the
    // opaque/clipping rectangle if there is one.
    //
    if (fExtTextOut)
    {
        //
        // If the rectangle is an opaque rectangle then expand the bounding
        // rectangle to bound the opaque rectangle also.
        //
        if (pExtTextOut->fuOptions & ETO_OPAQUE)
        {
            pRect->left   = min(pExtTextOut->rectangle.left, pRect->left);
            pRect->right  = max(pExtTextOut->rectangle.right,
                                   pRect->right);
            pRect->top    = min(pExtTextOut->rectangle.top,
                                   pRect->top);
            pRect->bottom = max(pExtTextOut->rectangle.bottom,
                                   pRect->bottom);
        }

        //
        // If the rectangle is a clip rectangle then restrict the bounding
        // rectangle to be within the clip rectangle.
        //
        if (pExtTextOut->fuOptions & ETO_CLIPPED)
        {
            pRect->left   = max(pExtTextOut->rectangle.left,
                                   pRect->left);
            pRect->right  = min(pExtTextOut->rectangle.right,
                                   pRect->right);
            pRect->top    = max(pExtTextOut->rectangle.top,
                                   pRect->top);
            pRect->bottom = min(pExtTextOut->rectangle.bottom,
                                   pRect->bottom);
        }
    }

    DebugExitVOID(ASShare::OD2_CalculateTextOutBounds);
}


//
// OD2_CalculateBounds()
//
void  ASShare::OD2_CalculateBounds
(
    LPCOM_ORDER     pOrder,
    LPRECT          pRect,
    BOOL            fDecoding,
    ASPerson *      pasPerson
)
{
    UINT            i;
    UINT            numPoints;

    DebugEntry(ASShare::OD2_CalculateBounds);

    ValidatePerson(pasPerson);

    //
    // Calculate the bounds according to the order type.
    // All blts can be handled in the same way.
    //
    switch ( ((LPPATBLT_ORDER)pOrder->abOrderData)->type )
    {
        //
        // Calculate bounds for the blts.
        // This is the destination rectangle. Bounds are inclusive.
        //
        case ORD_DSTBLT_TYPE:

            pRect->left   =
                           ((LPDSTBLT_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top    = ((LPDSTBLT_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->right  = pRect->left
                          + ((LPDSTBLT_ORDER)(pOrder->abOrderData))->nWidth
                          - 1;
            pRect->bottom = pRect->top
                          + ((LPDSTBLT_ORDER)(pOrder->abOrderData))->nHeight
                          - 1;
            break;


        case ORD_PATBLT_TYPE:

            pRect->left =
                  ((LPPATBLT_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top =
                  ((LPPATBLT_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->right =
                  pRect->left +
                  ((LPPATBLT_ORDER)(pOrder->abOrderData))->nWidth - 1;
            pRect->bottom =
                  pRect->top +
                  ((LPPATBLT_ORDER)(pOrder->abOrderData))->nHeight - 1;
            break;


        case ORD_SCRBLT_TYPE:

            pRect->left =
                  ((LPSCRBLT_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top =
                  ((LPSCRBLT_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->right =
                  pRect->left +
                  ((LPSCRBLT_ORDER)(pOrder->abOrderData))->nWidth - 1;
            pRect->bottom =
                  pRect->top +
                  ((LPSCRBLT_ORDER)(pOrder->abOrderData))->nHeight - 1;
            break;

        case ORD_MEMBLT_TYPE:

            pRect->left =
                  ((LPMEMBLT_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top =
                  ((LPMEMBLT_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->right =
                  pRect->left +
                  ((LPMEMBLT_ORDER)(pOrder->abOrderData))->nWidth - 1;
            pRect->bottom =
                  pRect->top +
                  ((LPMEMBLT_ORDER)(pOrder->abOrderData))->nHeight - 1;
            break;

        case ORD_MEM3BLT_TYPE:

            pRect->left =
                  ((LPMEM3BLT_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top =
                  ((LPMEM3BLT_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->right =
                  pRect->left +
                  ((LPMEM3BLT_ORDER)(pOrder->abOrderData))->nWidth - 1;
            pRect->bottom =
                  pRect->top +
                  ((LPMEM3BLT_ORDER)(pOrder->abOrderData))->nHeight - 1;
            break;

        case ORD_MEMBLT_R2_TYPE:
            pRect->left =
                  ((LPMEMBLT_R2_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top =
                  ((LPMEMBLT_R2_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->right =
                  pRect->left +
                  ((LPMEMBLT_R2_ORDER)(pOrder->abOrderData))->nWidth - 1;
            pRect->bottom =
                  pRect->top +
                  ((LPMEMBLT_R2_ORDER)(pOrder->abOrderData))->nHeight - 1;
            break;

        case ORD_MEM3BLT_R2_TYPE:
            pRect->left =
                  ((LPMEM3BLT_R2_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top =
                  ((LPMEM3BLT_R2_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->right =
                  pRect->left +
                  ((LPMEM3BLT_R2_ORDER)(pOrder->abOrderData))->nWidth - 1;
            pRect->bottom =
                  pRect->top +
                  ((LPMEM3BLT_R2_ORDER)(pOrder->abOrderData))->nHeight - 1;
            break;

        //
        // Calculate bounds for Rectangle.
        // This is the rectangle itself. Bounds are inclusive.
        //
        case ORD_RECTANGLE_TYPE:

            pRect->left =
                  ((LPRECTANGLE_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top =
                  ((LPRECTANGLE_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->right =
                  ((LPRECTANGLE_ORDER)(pOrder->abOrderData))->nRightRect;
            pRect->bottom =
                  ((LPRECTANGLE_ORDER)(pOrder->abOrderData))->nBottomRect;
            break;


        case ORD_ROUNDRECT_TYPE:

            pRect->left =
                  ((LPROUNDRECT_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top =
                  ((LPROUNDRECT_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->right =
                  ((LPROUNDRECT_ORDER)(pOrder->abOrderData))->nRightRect;
            pRect->bottom =
                  ((LPROUNDRECT_ORDER)(pOrder->abOrderData))->nBottomRect;
            break;

        case ORD_POLYGON_TYPE:
            //
            // Calculate bounds for Polygon.
            //
            pRect->left = 0x7fff;
            pRect->right = 0;
            pRect->top = 0x7fff;
            pRect->bottom = 0;

            //
            // BOGUS! LAURABU BUGBUG
            //
            // In NM 2.0, the wrong fields were being compared.  x to top/
            // bottom, and y to left/right.
            //
            // Effectively, this meant that we never matched the bounds
            // in the rcsDst rect.
            //
            numPoints = ((LPPOLYGON_ORDER)(pOrder->abOrderData))->
                        variablePoints.len
                    / sizeof(((LPPOLYGON_ORDER)(pOrder->abOrderData))->
                        variablePoints.aPoints[0]);

            for (i = 0; i < numPoints; i++ )
            {
                if ( ((LPPOLYGON_ORDER)(pOrder->abOrderData))
                        ->variablePoints.aPoints[i].y > pRect->bottom )
                {
                    pRect->bottom = ((LPPOLYGON_ORDER)(pOrder->abOrderData))
                        ->variablePoints.aPoints[i].y;
                }

                if ( ((LPPOLYGON_ORDER)(pOrder->abOrderData))
                        ->variablePoints.aPoints[i].y < pRect->top )
                {
                    pRect->top = ((LPPOLYGON_ORDER)(pOrder->abOrderData))
                        ->variablePoints.aPoints[i].y;
                }

                if ( ((LPPOLYGON_ORDER)(pOrder->abOrderData))
                        ->variablePoints.aPoints[i].x > pRect->right )
                {
                    pRect->right = ((LPPOLYGON_ORDER)(pOrder->abOrderData))
                        ->variablePoints.aPoints[i].x;
                }

                if ( ((LPPOLYGON_ORDER)(pOrder->abOrderData))
                        ->variablePoints.aPoints[i].x < pRect->left )
                {
                    pRect->left = ((LPPOLYGON_ORDER)(pOrder->abOrderData))
                        ->variablePoints.aPoints[i].x;
                }
            }

            TRACE_OUT(("Poly bounds: left:%d, right:%d, top:%d, bottom:%d",
                pRect->left, pRect->right, pRect->top, pRect->bottom ));

            break;

        case ORD_PIE_TYPE:
            //
            // Pull out the bounding rectangle directly from the PIE order.
            //

            pRect->left = ((LPPIE_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top = ((LPPIE_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->right = ((LPPIE_ORDER)(pOrder->abOrderData))->nRightRect;
            pRect->bottom = ((LPPIE_ORDER)(pOrder->abOrderData))->nBottomRect;

            break;

        case ORD_ELLIPSE_TYPE:
            //
            // Pull out the bounding rectangle directly from ELLIPSE order.
            //
            pRect->left = ((LPELLIPSE_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top = ((LPELLIPSE_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->right =
                         ((LPELLIPSE_ORDER)(pOrder->abOrderData))->nRightRect;
            pRect->bottom =
                        ((LPELLIPSE_ORDER)(pOrder->abOrderData))->nBottomRect;

            break;

        case ORD_ARC_TYPE:
            //
            // Pull out the bounding rectangle directly from the ARC order.
            //
            pRect->left = ((LPARC_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top = ((LPARC_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->right = ((LPARC_ORDER)(pOrder->abOrderData))->nRightRect;
            pRect->bottom = ((LPARC_ORDER)(pOrder->abOrderData))->nBottomRect;

            break;

        case ORD_CHORD_TYPE:
            //
            // Pull out the bounding rectangle directly from the CHORD
            // order.
            //
            pRect->left = ((LPCHORD_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top = ((LPCHORD_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->right = ((LPCHORD_ORDER)(pOrder->abOrderData))->nRightRect;
            pRect->bottom =
                          ((LPCHORD_ORDER)(pOrder->abOrderData))->nBottomRect;

            break;


        case ORD_POLYBEZIER_TYPE:
            //
            // Calculate bounds for PolyBezier.
            //
            pRect->left = 0x7fff;
            pRect->right = 0;
            pRect->top = 0x7fff;
            pRect->bottom = 0;

            numPoints = ((LPPOLYBEZIER_ORDER)(pOrder->abOrderData))->
                        variablePoints.len
                    / sizeof(((LPPOLYBEZIER_ORDER)(pOrder->abOrderData))->
                        variablePoints.aPoints[0]);

            //
            // BOGUS! LAURABU BUGBUG
            //
            // In NM 2.0, the wrong fields were being compared.  x to top/
            // bottom, and y to left/right.
            //
            // Effectively, this meant that we never matched the bounds
            // in the rcsDst rect.
            //
            for (i = 0; i < numPoints; i++ )
            {
                if ( ((LPPOLYBEZIER_ORDER)(pOrder->abOrderData))
                        ->variablePoints.aPoints[i].y > pRect->bottom )
                {
                    pRect->bottom = ((LPPOLYBEZIER_ORDER)(pOrder->abOrderData))
                                   ->variablePoints.aPoints[i].y;
                }

                if ( ((LPPOLYBEZIER_ORDER)(pOrder->abOrderData))
                        ->variablePoints.aPoints[i].y < pRect->top )
                {
                    pRect->top = ((LPPOLYBEZIER_ORDER)(pOrder->abOrderData))
                                 ->variablePoints.aPoints[i].y;
                }

                if ( ((LPPOLYBEZIER_ORDER)(pOrder->abOrderData))
                        ->variablePoints.aPoints[i].x > pRect->right )
                {
                    pRect->right = ((LPPOLYBEZIER_ORDER)(pOrder->abOrderData))
                        ->variablePoints.aPoints[i].x;
                }

                if ( ((LPPOLYBEZIER_ORDER)(pOrder->abOrderData))
                        ->variablePoints.aPoints[i].x < pRect->left )
                {
                    pRect->left = ((LPPOLYBEZIER_ORDER)(pOrder->abOrderData))
                        ->variablePoints.aPoints[i].x;
                }
            }

            TRACE_OUT((
                     "PolyBezier bounds: left:%d, right:%d, top:%d, bot:%d",
                     pRect->left, pRect->right, pRect->top, pRect->bottom));
            break;


        case ORD_LINETO_TYPE:
            //
            // Calculate bounds for LineTo.  This is the rectangle with
            // opposite vertices on the start and end points of the line.
            // The gradient of the line determines whether the start or end
            // point provides the top or bottom, left or right of the
            // rectangle.  Bounds are inclusive.
            //
            if ( ((LPLINETO_ORDER)(pOrder->abOrderData))->nXStart <
                  ((LPLINETO_ORDER)(pOrder->abOrderData))->nXEnd )
            {
                pRect->left =
                      ((LPLINETO_ORDER)(pOrder->abOrderData))->nXStart;
                pRect->right =
                      ((LPLINETO_ORDER)(pOrder->abOrderData))->nXEnd;
            }
            else
            {
                pRect->right =
                      ((LPLINETO_ORDER)pOrder->abOrderData)->nXStart;
                pRect->left =
                      ((LPLINETO_ORDER)pOrder->abOrderData)->nXEnd;
            }

            if ( ((LPLINETO_ORDER)pOrder->abOrderData)->nYStart <
                  ((LPLINETO_ORDER)pOrder->abOrderData)->nYEnd )
            {
                pRect->top =
                      ((LPLINETO_ORDER)pOrder->abOrderData)->nYStart;
                pRect->bottom =
                      ((LPLINETO_ORDER)pOrder->abOrderData)->nYEnd;
            }
            else
            {
                pRect->bottom =
                      ((LPLINETO_ORDER)pOrder->abOrderData)->nYStart;
                pRect->top =
                      ((LPLINETO_ORDER)pOrder->abOrderData)->nYEnd;
            }
            break;

        case ORD_OPAQUERECT_TYPE:
            //
            // Calculate bounds for OpaqueRect.  This is the rectangle
            // itself.  Bounds are inclusive.
            //
            pRect->left =
                  ((LPOPAQUERECT_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top =
                  ((LPOPAQUERECT_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->right =
                  pRect->left +
                  ((LPOPAQUERECT_ORDER)(pOrder->abOrderData))->nWidth - 1;
            pRect->bottom =
                  pRect->top +
                  ((LPOPAQUERECT_ORDER)(pOrder->abOrderData))->nHeight - 1;
            break;

        case ORD_SAVEBITMAP_TYPE:
            //
            // Calculate bounds for SaveBitmap.  This is the rectangle
            // itself.  Bounds are inclusive.
            //
            pRect->left =
                  ((LPSAVEBITMAP_ORDER)(pOrder->abOrderData))->nLeftRect;
            pRect->top =
                  ((LPSAVEBITMAP_ORDER)(pOrder->abOrderData))->nTopRect;
            pRect->bottom =
                  ((LPSAVEBITMAP_ORDER)(pOrder->abOrderData))->nBottomRect;
            pRect->right =
                  ((LPSAVEBITMAP_ORDER)(pOrder->abOrderData))->nRightRect;
            break;


        case ORD_TEXTOUT_TYPE:
        case ORD_EXTTEXTOUT_TYPE:
            //
            // TextOut and ExtTextOut bounds calculations are done by the
            // OD2_CalculateTextOutBounds function.
            //
            OD2_CalculateTextOutBounds((LPTEXTOUT_ORDER)pOrder->abOrderData,
                                      pRect,
                                      fDecoding,
                                      pasPerson);
            break;


        case ORD_DESKSCROLL_TYPE:
            pRect->left   = 0;
            pRect->top    = 0;
            pRect->right  = 0;
            pRect->bottom = 0;
            break;


        default:
            ERROR_OUT((
                "{%p} unrecognized type passed to OD2ReconstructBounds: %d",
                       pasPerson,
                       (int)((LPPATBLT_ORDER)pOrder->abOrderData)->type));
            break;
    }

    DebugExitVOID(ASShare::OD2_CalculateBounds);
}




//
// OD2DecodeBounds()
//
void  ASShare::OD2DecodeBounds
(
    LPBYTE*         ppNextDataToCopy,
    LPTSHR_RECT16   pRect,
    ASPerson *      pasPerson
)
{
    LPBYTE pFlags;

    DebugEntry(ASShare::OD2DecodeBounds);

    ValidatePerson(pasPerson);

    //
    // The encoding used is a byte of flags followed by a variable number
    // of 16bit coordinate values and 8bit delta coordinate values (which
    // may be interleaved).
    //

    //
    // The first byte of the encoding will contain the flags that represent
    // how the coordinates of the rectangle were encoded.
    //
    pFlags = *ppNextDataToCopy;
    (*ppNextDataToCopy)++;

    //
    // Initialise the rectangle with the last decoded coordinates.
    //
    *pRect = pasPerson->od2Party->LastBounds;

    //
    // If the flags indicate that none of the coordinates have changed then
    // fast path and exit now.
    //
    if (*pFlags == 0)
    {
        return;
    }

    //
    // For each of the four coordinate values in the rectangle: If the
    // coordinate was encoded as an 8bit delta then add on the delta to the
    // previous value.  If the coordinate was encoded as a 16bit value
    // then copy the value across. Otherwise the coordinate was the same
    // as the previous one so leave it alone.
    //
    if (*pFlags & OE2_BCF_DELTA_LEFT)
    {
        OD2CopyFromDeltaCoords((LPTSHR_INT8*)ppNextDataToCopy,
                               &pRect->left,
                               sizeof(pRect->left),
                               TRUE,        // The value is signed
                               1);
    }
    else if (*pFlags & OE2_BCF_LEFT)
    {
        pRect->left          = EXTRACT_TSHR_INT16_UA(*ppNextDataToCopy);
        (*ppNextDataToCopy) += sizeof(TSHR_INT16);
    }

    if (*pFlags & OE2_BCF_DELTA_TOP)
    {
        OD2CopyFromDeltaCoords((LPTSHR_INT8*)ppNextDataToCopy,
                               &pRect->top,
                               sizeof(pRect->top),
                               TRUE,        // The value is signed
                               1);
    }
    else if (*pFlags & OE2_BCF_TOP)
    {
        pRect->top           = EXTRACT_TSHR_INT16_UA(*ppNextDataToCopy);
        (*ppNextDataToCopy) += sizeof(TSHR_INT16);
    }

    if (*pFlags & OE2_BCF_DELTA_RIGHT)
    {
        OD2CopyFromDeltaCoords((LPTSHR_INT8*)ppNextDataToCopy,
                               &pRect->right,
                               sizeof(pRect->right),
                               TRUE,        // The value is signed
                               1);
    }
    else if (*pFlags & OE2_BCF_RIGHT)
    {
        pRect->right         = EXTRACT_TSHR_INT16_UA(*ppNextDataToCopy);
        (*ppNextDataToCopy) += sizeof(TSHR_INT16);
    }

    if (*pFlags & OE2_BCF_DELTA_BOTTOM)
    {
        OD2CopyFromDeltaCoords((LPTSHR_INT8*)ppNextDataToCopy,
                               &pRect->bottom,
                               sizeof(pRect->bottom),
                               TRUE,        // The value is signed
                               1);
    }
    else if (*pFlags & OE2_BCF_BOTTOM)
    {
        pRect->bottom        = EXTRACT_TSHR_INT16_UA(*ppNextDataToCopy);
        (*ppNextDataToCopy) += sizeof(TSHR_INT16);
    }

    //
    // Copy the rectangle for reference with the next encoding.
    //
    pasPerson->od2Party->LastBounds = *pRect;

    DebugExitVOID(ASShare::OD2DecodeBounds);
}


//
// Copy an array of source elements to an array of destination elements,
// converting the types as the copy takes place.
//
//   DESTARRAY   - The destination array
//   SRCARRAY    - The source array
//   DESTTYPE    - The type of the elements in the destination array
//   NUMELEMENTS - The number of elements in the array
//
//
#define CONVERT_ARRAY(DESTARRAY, SRCARRAY, DESTTYPE, NUMELEMENTS)     \
{                                                           \
    UINT index;                                           \
    for (index=0 ; index<(NUMELEMENTS) ; index++)           \
    {                                                       \
        (DESTARRAY)[index] = (DESTTYPE)(SRCARRAY)[index];   \
    }                                                       \
}

//
// Copy an array of source elements to an array of destination elements,
// converting the types as the copy takes place. This version allows for
// unaligned INT16 pointers
//
//   DESTARRAY   - The destination array
//   SRCARRAY    - The source array
//   DESTTYPE    - The type of the elements in the destination array
//   NUMELEMENTS - The number of elements in the array
//
//
#define CONVERT_ARRAY_INT16_UA(DESTARRAY, SRCARRAY, DESTTYPE, NUMELEMENTS)   \
{                                                           \
    UINT index;                                           \
    TSHR_INT16 value;                                          \
    for (index=0 ; index<(NUMELEMENTS) ; index++)           \
    {                                                       \
        value = EXTRACT_TSHR_INT16_UA((SRCARRAY)+index);      \
        (DESTARRAY)[index] = (DESTTYPE)value;               \
    }                                                       \
}

//
// Copy an array of source elements to an array of destination elements,
// converting the types as the copy takes place. This version allows for
// unaligned TSHR_UINT16 pointers
//
//   DESTARRAY   - The destination array
//   SRCARRAY    - The source array
//   DESTTYPE    - The type of the elements in the destination array
//   NUMELEMENTS - The number of elements in the array
//
//
#define CONVERT_ARRAY_UINT16_UA(DESTARRAY, SRCARRAY, DESTTYPE, NUMELEMENTS)  \
{                                                                            \
    UINT index;                                                            \
    TSHR_UINT16 value;                                                          \
    for (index=0 ; index<(NUMELEMENTS) ; index++)                            \
    {                                                                        \
        value = EXTRACT_TSHR_UINT16_UA((SRCARRAY)+index);                      \
        (DESTARRAY)[index] = (DESTTYPE)((TSHR_INT16)value);                    \
    }                                                                        \
}

//
// OD2DecodeField()
//
void  ASShare::OD2DecodeField
(
    LPBYTE*     ppSrc,
    LPVOID      pDst,
    UINT        cbSrcField,
    UINT        cbDstField,
    BOOL        fSigned,
    UINT        numElements
)
{
    LPTSHR_UINT8    pDst8          = (LPTSHR_UINT8)pDst;
    LPTSHR_INT16    pDst16Signed   = (LPTSHR_INT16)pDst;
    LPTSHR_INT32    pDst32Signed   = (LPTSHR_INT32)pDst;
    LPTSHR_UINT16   pDst16Unsigned = (LPTSHR_UINT16)pDst;
    LPTSHR_UINT32   pDst32Unsigned = (LPTSHR_UINT32)pDst;
    LPTSHR_INT8     pSrc8Signed     = (LPTSHR_INT8)*ppSrc;
    LPTSHR_UINT8    pSrc8Unsigned   = (LPTSHR_UINT8)*ppSrc;
    LPTSHR_INT16_UA pSrc16Signed    = (LPTSHR_INT16_UA)*ppSrc;
    LPTSHR_UINT16_UA pSrc16Unsigned  = (LPTSHR_UINT16_UA)*ppSrc;

    //
    // Note that the source fields may not be aligned correctly, so we use
    // unaligned pointers.  The destination is aligned correctly.
    //
    DebugEntry(ASShare::OD2DecodeField);

    //
    // Make sure that the destination field length is larger or equal to
    // the source field length.  If it isn't, something has gone wrong.
    //
    if (cbDstField < cbSrcField)
    {
        ERROR_OUT(( "Source field length %d is larger than destination %d",
                     cbSrcField,
                     cbDstField));
        DC_QUIT;
    }

    //
    // If the source and destination field lengths are the same, we can
    // just do a copy (no type conversion required).
    //
    if (cbSrcField == cbDstField)
    {
        memcpy(pDst8, *ppSrc, cbDstField * numElements);
    }
    else
    {
        //
        // We know that cbDstField must be greater than cbSrcField
        // because of our checks above.  So there are only three
        // conversions to consider:
        //
        //    8 bit -> 16 bit
        //    8 bit -> 32 bit
        //   16 bit -> 32 bit
        //
        // We also have to get the signed / unsigned attributes correct. If
        // we try to promote a signed value using unsigned pointers, we
        // will get the wrong result.
        //
        // e.g. Consider converting the value -1 from a TSHR_INT16 to TSHR_INT32
        //      using unsigned pointers.
        //
        //      -1 -> TSHR_UINT16 == 65535
        //         -> UINT == 65535
        //         -> TSHR_INT32  == 65535
        //
        //
        if ((cbDstField == 4) && (cbSrcField == 1))
        {
            if (fSigned)
            {
                CONVERT_ARRAY(pDst32Signed,
                              pSrc8Signed,
                              TSHR_INT32,
                              numElements);
            }
            else
            {
                CONVERT_ARRAY(pDst32Unsigned,
                              pSrc8Unsigned,
                              TSHR_UINT32,
                              numElements);
            }
        }
        else if ((cbDstField == 4) && (cbSrcField == 2))
        {
            if (fSigned)
            {
                CONVERT_ARRAY_INT16_UA(pDst32Signed,
                                       pSrc16Signed,
                                       TSHR_INT32,
                                       numElements);
            }
            else
            {
                CONVERT_ARRAY_UINT16_UA(pDst32Unsigned,
                                        pSrc16Unsigned,
                                        TSHR_UINT32,
                                        numElements);
            }
        }
        else if ((cbDstField == 2) && (cbSrcField == 1))
        {
            if (fSigned)
            {
                CONVERT_ARRAY(pDst16Signed,
                              pSrc8Signed,
                              TSHR_INT16,
                              numElements);
            }
            else
            {
                CONVERT_ARRAY(pDst16Unsigned,
                              pSrc8Unsigned,
                              TSHR_UINT16,
                              numElements);
            }
        }
        else
        {
            ERROR_OUT(( "Bad conversion, dest length = %d, src length = %d",
                         cbDstField,
                         cbSrcField));
        }
    }

DC_EXIT_POINT:
    *ppSrc += cbSrcField * numElements;
    DebugExitVOID(ASShare::OD2DecodeField);
}



//
// Given two arrays, a source array and an array of deltas, add each delta
// to the corresponding element in the source array, storing the results in
// the source array.
//
//   srcArray     - The array of source values
//   srcArrayType - The type of the array of source values
//   deltaArray   - The array of deltas
//   numElements  - The number of elements in the arrays
//
//
#define COPY_DELTA_ARRAY(srcArray, srcArrayType, deltaArray, numElements)  \
{                                                            \
    UINT index;                                            \
    for (index = 0; index < (numElements); index++)          \
    {                                                        \
        (srcArray)[index] = (srcArrayType)                   \
           ((srcArray)[index] + (deltaArray)[index]);        \
    }                                                        \
}


//
// OD2CopyFromDeltaCoords()
//
void  ASShare::OD2CopyFromDeltaCoords
(
    LPTSHR_INT8*    ppSrc,
    LPVOID          pDst,
    UINT            cbDstField,
    BOOL            fSigned,
    UINT            numElements
)
{
    LPTSHR_INT8     pDst8Signed    = (LPTSHR_INT8)pDst;
    LPTSHR_INT16    pDst16Signed   = (LPTSHR_INT16)pDst;
    LPTSHR_INT32    pDst32Signed   = (LPTSHR_INT32)pDst;
    LPTSHR_UINT8    pDst8Unsigned  = (LPTSHR_UINT8)pDst;
    LPTSHR_UINT16   pDst16Unsigned = (LPTSHR_UINT16)pDst;
    LPTSHR_UINT32   pDst32Unsigned = (LPTSHR_UINT32)pDst;

    DebugEntry(ASShare::OD2CopyFromDeltaCoords);

    switch (cbDstField)
    {
        case 1:
            if (fSigned)
            {
                COPY_DELTA_ARRAY(pDst8Signed, TSHR_INT8, *ppSrc, numElements);
            }
            else
            {
                COPY_DELTA_ARRAY(pDst8Unsigned, TSHR_UINT8, *ppSrc, numElements);
            }
            break;

        case 2:
            if (fSigned)
            {
                COPY_DELTA_ARRAY(pDst16Signed, TSHR_INT16, *ppSrc, numElements);
            }
            else
            {
                COPY_DELTA_ARRAY(pDst16Unsigned, TSHR_UINT16, *ppSrc, numElements);
            }
            break;

        case 4:
            if (fSigned)
            {
                COPY_DELTA_ARRAY(pDst32Signed, TSHR_INT32, *ppSrc, numElements);
            }
            else
            {
                COPY_DELTA_ARRAY(pDst32Unsigned, TSHR_UINT32, *ppSrc, numElements);
            }
            break;

        default:
            ERROR_OUT(( "Bad destination field length %d",
                         cbDstField));
            DC_QUIT;
            // break;
    }

DC_EXIT_POINT:
    *ppSrc += numElements;
    DebugExitVOID(ASShare::OD2CopyFromDeltaCoords);
}


