#include "precomp.h"


//
// OE2.CPP
// Order Encoding Second Level
//
// Copyright(c) Microsoft 1997-
//

#define MLZ_FILE_ZONE  ZONE_ORDER

//
// OE2_HostStarting()
//
BOOL  ASHost::OE2_HostStarting(void)
{
    DebugEntry(ASHost::OE2_HostStarting);

    //
    // Set up the pointers for 2nd level encoding
    //
    m_oe2Tx.LastOrder[OE2_DSTBLT_ORDER    ] = &m_oe2Tx.LastDstblt;
    m_oe2Tx.LastOrder[OE2_PATBLT_ORDER    ] = &m_oe2Tx.LastPatblt;
    m_oe2Tx.LastOrder[OE2_SCRBLT_ORDER    ] = &m_oe2Tx.LastScrblt;
    m_oe2Tx.LastOrder[OE2_MEMBLT_ORDER    ] = &m_oe2Tx.LastMemblt;
    m_oe2Tx.LastOrder[OE2_MEM3BLT_ORDER   ] = &m_oe2Tx.LastMem3blt;
    m_oe2Tx.LastOrder[OE2_TEXTOUT_ORDER   ] = &m_oe2Tx.LastTextOut;
    m_oe2Tx.LastOrder[OE2_EXTTEXTOUT_ORDER] = &m_oe2Tx.LastExtTextOut;
    m_oe2Tx.LastOrder[OE2_RECTANGLE_ORDER ] = &m_oe2Tx.LastRectangle;
    m_oe2Tx.LastOrder[OE2_LINETO_ORDER    ] = &m_oe2Tx.LastLineTo;
    m_oe2Tx.LastOrder[OE2_OPAQUERECT_ORDER] = &m_oe2Tx.LastOpaqueRect;
    m_oe2Tx.LastOrder[OE2_SAVEBITMAP_ORDER] = &m_oe2Tx.LastSaveBitmap;
    m_oe2Tx.LastOrder[OE2_DESKSCROLL_ORDER] = &m_oe2Tx.LastDeskScroll;
    m_oe2Tx.LastOrder[OE2_MEMBLT_R2_ORDER ] = &m_oe2Tx.LastMembltR2;
    m_oe2Tx.LastOrder[OE2_MEM3BLT_R2_ORDER] = &m_oe2Tx.LastMem3bltR2;
    m_oe2Tx.LastOrder[OE2_POLYGON_ORDER   ] = &m_oe2Tx.LastPolygon;
    m_oe2Tx.LastOrder[OE2_PIE_ORDER       ] = &m_oe2Tx.LastPie;
    m_oe2Tx.LastOrder[OE2_ELLIPSE_ORDER   ] = &m_oe2Tx.LastEllipse;
    m_oe2Tx.LastOrder[OE2_ARC_ORDER       ] = &m_oe2Tx.LastArc;
    m_oe2Tx.LastOrder[OE2_CHORD_ORDER     ] = &m_oe2Tx.LastChord;
    m_oe2Tx.LastOrder[OE2_POLYBEZIER_ORDER] = &m_oe2Tx.LastPolyBezier;
    m_oe2Tx.LastOrder[OE2_ROUNDRECT_ORDER ] = &m_oe2Tx.LastRoundRect;

    //
    // Set up the last order values to a known value.
    //
    m_oe2Tx.LastOrderType = OE2_PATBLT_ORDER;
    m_oe2Tx.pLastOrder = (LPCOM_ORDER)m_oe2Tx.LastOrder[m_oe2Tx.LastOrderType];

    DebugExitBOOL(ASHost::OE2_HostStarting, TRUE);
    return(TRUE);
}


//
// OE2_HostEnded()
//
void ASHost::OE2_HostEnded(void)
{
    DebugEntry(ASHost::OE2_HostEnded);

    //
    // For OUTGOING order encoding, free the last font we cached.
    //
    if (m_oe2Tx.LastHFONT != NULL)
    {
        ASSERT(m_pShare);
        ASSERT(m_usrWorkDC);

        SelectFont(m_usrWorkDC, (HFONT)GetStockObject(SYSTEM_FONT));

        DeleteFont(m_oe2Tx.LastHFONT);
        m_oe2Tx.LastHFONT = NULL;
    }

    DebugExitVOID(ASHost::OE2_HostEnded);
}


//
// OE2_SyncOutgoing()
// Called when NEW dude starts to host, a share is created, or somebody new
// joins the share.
// Resets the OUTGOING 2nd level order encoding data.
//
void  ASHost::OE2_SyncOutgoing(void)
{
    DebugEntry(ASHost::OE2_SyncOutgoing);

    //
    // Set up the last order values to a known value.
    //
    m_oe2Tx.LastOrderType = OE2_PATBLT_ORDER;
    m_oe2Tx.pLastOrder = (LPCOM_ORDER)m_oe2Tx.LastOrder[m_oe2Tx.LastOrderType];

    //
    // Clear out all the last orders.
    //
    ZeroMemory(&m_oe2Tx.LastDstblt, sizeof(m_oe2Tx.LastDstblt));
    ((PATBLT_ORDER*)&m_oe2Tx.LastDstblt)->type = ORD_DSTBLT_TYPE;

    ZeroMemory(&m_oe2Tx.LastPatblt, sizeof(m_oe2Tx.LastPatblt));
    ((PATBLT_ORDER*)&m_oe2Tx.LastPatblt)->type = ORD_PATBLT_TYPE;

    ZeroMemory(&m_oe2Tx.LastScrblt, sizeof(m_oe2Tx.LastScrblt));
    ((PATBLT_ORDER*)&m_oe2Tx.LastScrblt)->type = ORD_SCRBLT_TYPE;

    ZeroMemory(&m_oe2Tx.LastMemblt, sizeof(m_oe2Tx.LastMemblt));
    ((PATBLT_ORDER*)&m_oe2Tx.LastMemblt)->type = ORD_MEMBLT_TYPE;

    ZeroMemory(&m_oe2Tx.LastMem3blt,sizeof(m_oe2Tx.LastMem3blt));
    ((PATBLT_ORDER*)&m_oe2Tx.LastMem3blt)->type = ORD_MEM3BLT_TYPE;

    ZeroMemory(&m_oe2Tx.LastTextOut, sizeof(m_oe2Tx.LastTextOut));
    ((PATBLT_ORDER*)&m_oe2Tx.LastTextOut)->type = ORD_TEXTOUT_TYPE;

    ZeroMemory(&m_oe2Tx.LastExtTextOut, sizeof(m_oe2Tx.LastExtTextOut));
    ((PATBLT_ORDER*)&m_oe2Tx.LastExtTextOut)->type = ORD_EXTTEXTOUT_TYPE;

    ZeroMemory(&m_oe2Tx.LastRectangle, sizeof(m_oe2Tx.LastRectangle));
    ((PATBLT_ORDER*)&m_oe2Tx.LastRectangle)->type = ORD_RECTANGLE_TYPE;

    ZeroMemory(&m_oe2Tx.LastPolygon, sizeof(m_oe2Tx.LastPolygon));
    ((POLYGON_ORDER*)&m_oe2Tx.LastPolygon)->type = ORD_POLYGON_TYPE;

    ZeroMemory(&m_oe2Tx.LastPie, sizeof(m_oe2Tx.LastPie));
    ((PIE_ORDER*)&m_oe2Tx.LastPie)->type = ORD_PIE_TYPE;

    ZeroMemory(&m_oe2Tx.LastEllipse, sizeof(m_oe2Tx.LastEllipse));
    ((ELLIPSE_ORDER*)&m_oe2Tx.LastEllipse)->type = ORD_ELLIPSE_TYPE;

    ZeroMemory(&m_oe2Tx.LastArc, sizeof(m_oe2Tx.LastArc));
    ((ARC_ORDER*)&m_oe2Tx.LastArc)->type = ORD_ARC_TYPE;

    ZeroMemory(&m_oe2Tx.LastChord, sizeof(m_oe2Tx.LastChord));
    ((CHORD_ORDER*)&m_oe2Tx.LastChord)->type = ORD_CHORD_TYPE;

    ZeroMemory(&m_oe2Tx.LastPolyBezier, sizeof(m_oe2Tx.LastPolyBezier));
    ((POLYBEZIER_ORDER*)&m_oe2Tx.LastPolyBezier)->type = ORD_POLYBEZIER_TYPE;

    ZeroMemory(&m_oe2Tx.LastRoundRect, sizeof(m_oe2Tx.LastRoundRect));
    ((ROUNDRECT_ORDER*)&m_oe2Tx.LastRoundRect)->type = ORD_ROUNDRECT_TYPE;

    ZeroMemory(&m_oe2Tx.LastLineTo, sizeof(m_oe2Tx.LastLineTo));
    ((PATBLT_ORDER*)&m_oe2Tx.LastLineTo)->type = ORD_LINETO_TYPE;

    ZeroMemory(&m_oe2Tx.LastOpaqueRect, sizeof(m_oe2Tx.LastOpaqueRect));
    ((PATBLT_ORDER*)&m_oe2Tx.LastOpaqueRect)->type = ORD_OPAQUERECT_TYPE;

    ZeroMemory(&m_oe2Tx.LastSaveBitmap, sizeof(m_oe2Tx.LastSaveBitmap));
    ((PATBLT_ORDER*)&m_oe2Tx.LastSaveBitmap)->type = ORD_SAVEBITMAP_TYPE;

    ZeroMemory(&m_oe2Tx.LastDeskScroll, sizeof(m_oe2Tx.LastDeskScroll));
    ((PATBLT_ORDER*)&m_oe2Tx.LastDeskScroll)->type = ORD_DESKSCROLL_TYPE;

    ZeroMemory(&m_oe2Tx.LastMembltR2, sizeof(m_oe2Tx.LastMembltR2));
    ((PATBLT_ORDER*)&m_oe2Tx.LastMembltR2)->type = ORD_MEMBLT_R2_TYPE;

    ZeroMemory(&m_oe2Tx.LastMem3bltR2, sizeof(m_oe2Tx.LastMem3bltR2));
    ((PATBLT_ORDER*)&m_oe2Tx.LastMem3bltR2)->type = ORD_MEM3BLT_R2_TYPE;

    ZeroMemory(&m_oe2Tx.LastBounds, sizeof(m_oe2Tx.LastBounds));

    DebugExitVOID(ASHost::OE2_SyncOutgoing);
}


//
// OE2_EncodeOrder()
//
TSHR_UINT16  ASHost::OE2_EncodeOrder
(
    LPINT_ORDER     pIntOrder,
    void *          pBuffer,
    TSHR_UINT16     cbBufferSize
)
{
    POE2ETFIELD     pTableEntry;
    LPSTR           pNextFreeSpace;
    UINT            thisFlag = 0;
    RECT            Rect;
    TSHR_RECT16     Rect16;
    UINT            cbEncodedOrderSize;
    UINT            cbMaxEncodedOrderSize;
    LPBYTE          pControlFlags;
    LPTSHR_UINT32_UA pEncodingFlags;
    LPSTR           pEncodedOrder;
    UINT            numEncodingFlagBytes;
    LPSTR           pVariableField;
    BOOL            useDeltaCoords;
    UINT            i;
    LPCOM_ORDER     pComOrder;
    UINT            fieldLength;
    UINT            numReps;

    DebugEntry(ASHost::OE2_EncodeOrder);

#ifdef ORDER_TRACE
    if (OE2_DebugOrderTrace)
    {
        TrcUnencodedOrder(pIntOrder);
    }
#endif // ORDER_TRACE

    //
    // Set up a pointer to the Common Order.
    //
    pComOrder = (LPCOM_ORDER)&(pIntOrder->OrderHeader.Common);

    //
    // Calculate the maximum bytes required to encode this order.
    //
    if (pComOrder->OrderHeader.fOrderFlags & OF_PRIVATE)
    {
        //
        // Private order.
        //
        cbMaxEncodedOrderSize = OE2_CONTROL_FLAGS_FIELD_SIZE +
                                COM_ORDER_SIZE(pComOrder);
    }
    else
    {
        //
        // Normal (not Private) order.
        //
        cbMaxEncodedOrderSize = OE2_CONTROL_FLAGS_FIELD_SIZE +
                                OE2_TYPE_FIELD_SIZE +
                                OE2_MAX_FIELD_FLAG_BYTES +
                                OE2_MAX_ADDITIONAL_BOUNDS_BYTES +
                                COM_ORDER_SIZE(pComOrder);
    }

    //
    // If we are not absolutely certain that the supplied buffer is big
    // enough to hold this order (encoded) then return immediately.
    //
    if (cbMaxEncodedOrderSize > cbBufferSize)
    {
        cbEncodedOrderSize = 0;
        goto encode_order_exit;
    }

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

    //
    // Initialise the control flags field to indicate this is a standard
    // encoding (ie the rest of the control flags have the meaning defined
    // by the rest of the OE2_CF_XXX definitions).
    //
    *pControlFlags = OE2_CF_STANDARD_ENC;

    //
    // If the private flag is set then we must return the encoded order
    // as it is (ie without doing any further encoding).
    //
    if (pComOrder->OrderHeader.fOrderFlags & OF_PRIVATE)
    {
        *pControlFlags |= OE2_CF_UNENCODED;

        cbEncodedOrderSize = SBC_CopyPrivateOrderData(
                                   (LPBYTE)pEncodedOrder,
                                   pComOrder,
                                   cbMaxEncodedOrderSize -
                                     FIELD_OFFSET(DCEO2ORDER, EncodedOrder) );

        cbEncodedOrderSize += FIELD_OFFSET(DCEO2ORDER, EncodedOrder);

        TRACE_OUT(( "PRIVATE order size %u", cbEncodedOrderSize));

        goto encode_order_exit;
    }

    //
    // If the type of this order is different to the last order encoded,
    // get a pointer to the last order of this type encoded and remember
    // what type of order it is.  We must also tell the decoding end that
    // this type is different from the last one, so set the new type flag
    // and copy type into buffer
    //
    // The "type" field come before the encoding flags so that the number
    // of flags we have can vary depending on the order. Set up a pointer
    // to these flags here depending on whether or not we have to encode
    // the order type.
    //
    if (TEXTFIELD(pComOrder)->type != ((PATBLT_ORDER*)m_oe2Tx.pLastOrder)->type)
    {
        TRACE_OUT(( "change type from %04X to %04X",
                     LOWORD(((PATBLT_ORDER*)m_oe2Tx.pLastOrder)->type),
                     LOWORD(TEXTFIELD(pComOrder)->type)));

        m_oe2Tx.LastOrderType = OE2GetOrderType(pComOrder);
        m_oe2Tx.pLastOrder = (LPCOM_ORDER)m_oe2Tx.LastOrder[m_oe2Tx.LastOrderType];
        *(LPBYTE)pEncodedOrder = m_oe2Tx.LastOrderType;
        *pControlFlags |= OE2_CF_TYPE_CHANGE;
        pEncodingFlags = (LPTSHR_UINT32_UA)&pEncodedOrder[1];
    }
    else
    {
        pEncodingFlags = (LPTSHR_UINT32_UA)&pEncodedOrder[0];
    }

    //
    // 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[m_oe2Tx.LastOrderType]+7)/8;
    if (numEncodingFlagBytes > OE2_MAX_FIELD_FLAG_BYTES)
    {
        ERROR_OUT(( "Too many flag bytes (%d) for this code", 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.
    //
    pNextFreeSpace = ((LPSTR)pEncodingFlags) + numEncodingFlagBytes;

    //
    // Calculate the bounds.  If these are the same as those already in the
    // order header then there is no need to send any bounds because we can
    // recalculate them at the receiver.
    //
    m_pShare->OD2_CalculateBounds(pComOrder, &Rect, FALSE, m_pShare->m_pasLocal);
    TSHR_RECT16_FROM_RECT(&Rect16, Rect);
    if (memcmp(&(pComOrder->OrderHeader.rcsDst), &Rect16, sizeof(Rect16)))
    {
        TRACE_OUT(( "copy bounding rect"));
        OE2EncodeBounds((LPBYTE*)&pNextFreeSpace,
                        &pComOrder->OrderHeader.rcsDst);
        *pControlFlags |= OE2_CF_BOUNDS;
    }

    //
    // Before we do the field encoding (using s_etable) check all the field
    // entries flagged as coordinates to see if we can switch to
    // OE2_CF_DELTACOORDS mode.
    //
    pTableEntry = s_etable.pFields[m_oe2Tx.LastOrderType];

    useDeltaCoords = TRUE;

    //
    // Loop through each fixed field in this order structure...
    //
    while ( useDeltaCoords
              && (pTableEntry->FieldPos != 0)
              && ((pTableEntry->FieldType & OE2_ETF_FIXED) != 0) )
    {
        //
        // If this field entry is a coordinate then compare it to the
        // previous coordinate we sent for this field to determine whether
        // we can send it as a delta
        //
        if (pTableEntry->FieldType & OE2_ETF_COORDINATES)
        {
            useDeltaCoords =
                     OE2CanUseDeltaCoords(((LPSTR)pComOrder->abOrderData)
                                                     + pTableEntry->FieldPos,
                                          ((LPSTR)m_oe2Tx.pLastOrder)
                                                     + pTableEntry->FieldPos,
                                          pTableEntry->FieldUnencodedLen,
                                          pTableEntry->FieldSigned,
                                          1);
        }
        pTableEntry++;
    }

    //
    // Loop through each of the variable fields...
    //
    pVariableField = ((LPSTR)(pComOrder->abOrderData))
                   + pTableEntry->FieldPos;
    while (useDeltaCoords && (pTableEntry->FieldPos != 0))
    {
        //
        // The length of the field (in bytes) is given in the first
        // TSHR_UINT32 of the variable sized field structure.
        //
        fieldLength     = *(TSHR_UINT32 FAR *)pVariableField;
        pVariableField += sizeof(TSHR_UINT32);

        //
        // If this field entry is a coordinate then compare it to the
        // previous coordinate we sent for this field to determine whether
        // we can send it as a delta
        //
        if (pTableEntry->FieldType & OE2_ETF_COORDINATES)
        {
            //
            // The number of coordinates is given by the number of bytes in
            // the field divided by the size of each entry
            //
            numReps        = fieldLength / pTableEntry->FieldUnencodedLen;
            useDeltaCoords =
                     OE2CanUseDeltaCoords(pVariableField,
                                          ((LPSTR)m_oe2Tx.pLastOrder)
                                                     + pTableEntry->FieldPos,
                                          pTableEntry->FieldUnencodedLen,
                                          pTableEntry->FieldSigned,
                                          numReps);
        }

        //
        // Move on to the next field in the order structure.  Note that
        // variable sized fields are packed on the send side.  (ie
        // increment pVariableField by fieldLength not by
        // pTableEntry->FieldLen).
        //
        pVariableField += fieldLength;
        pTableEntry++;
    }

    if (useDeltaCoords)
    {
        *pControlFlags |= OE2_CF_DELTACOORDS;
    }

    //
    // Now do the encoding...
    //
    pTableEntry = s_etable.pFields[m_oe2Tx.LastOrderType];

    //
    // Clear the encoding flag bytes.
    //
    for (i = 0; i < numEncodingFlagBytes; i++)
    {
        ((LPBYTE)pEncodingFlags)[i] = 0;
    }

    thisFlag = 0x00000001;

    //
    // First process all the fixed size fields in the order structure...
    // (These come before the variable sized fields).
    //
    while (   (pTableEntry->FieldPos != 0)
           && (pTableEntry->FieldType & OE2_ETF_FIXED) )
    {
        //
        // If the field has changed since it was previously transmitted then
        // we need to send it again.
        //
        if (memcmp(
               ((LPBYTE)(pComOrder->abOrderData)) + pTableEntry->FieldPos,
               ((LPBYTE)m_oe2Tx.pLastOrder) + pTableEntry->FieldPos,
               pTableEntry->FieldUnencodedLen))
        {
            //
            // Update the encoding flags
            //
            *pEncodingFlags |= thisFlag;

            //
            // If we are encoding in delta coordinate mode and this field
            // is a coordinate...
            //
            if (useDeltaCoords &&
                      ((pTableEntry->FieldType & OE2_ETF_COORDINATES) != 0) )
            {
                OE2CopyToDeltaCoords((LPTSHR_INT8*)&pNextFreeSpace,
                                     (((LPSTR)pComOrder->abOrderData)
                                                     + pTableEntry->FieldPos),
                                     (((LPSTR)m_oe2Tx.pLastOrder)
                                                     + pTableEntry->FieldPos),
                                     pTableEntry->FieldUnencodedLen,
                                     pTableEntry->FieldSigned,
                                     1);
            }
            else
            {
                //
                // Update the data to be sent
                //
                OE2EncodeField(((LPBYTE)(pComOrder->abOrderData)) +
                                                       pTableEntry->FieldPos,
                               (LPBYTE*)&pNextFreeSpace,
                               pTableEntry->FieldUnencodedLen,
                               pTableEntry->FieldEncodedLen,
                               pTableEntry->FieldSigned,
                               1);

            }

            //
            // Save the current value for comparison next time.
            //
            memcpy(((LPBYTE)m_oe2Tx.pLastOrder) + pTableEntry->FieldPos,
                   ((LPBYTE)(pComOrder->abOrderData)) + pTableEntry->FieldPos,
                   pTableEntry->FieldUnencodedLen);
        }

        //
        // Move on to the next field in the structure.
        //
        thisFlag = thisFlag << 1;
        pTableEntry++;
    }

    //
    // Now process the variable sized entries...
    //
    pVariableField = ((LPSTR)(pComOrder->abOrderData))
                   + pTableEntry->FieldPos;
    while (pTableEntry->FieldPos != 0)
    {
        //
        // The length of the field is given in the first UINT of the
        // variable sized field structure.
        //
        fieldLength = *(TSHR_UINT32 FAR *)pVariableField;

        //
        // If the field has changed (either in size or in contents) then we
        // need to copy it across.
        //
        if (memcmp(pVariableField, ((LPBYTE)m_oe2Tx.pLastOrder) +
                    pTableEntry->FieldPos, fieldLength + sizeof(TSHR_UINT32)))
        {
            //
            // Update the encoding flags
            //
            *pEncodingFlags |= thisFlag;

            //
            // Work out how many elements we are encoding for this field.
            //
            numReps = fieldLength / pTableEntry->FieldUnencodedLen;

            //
            // Fill in the length of the field into the encoded buffer
            // (this is always encoded in a single byte), then increment
            // the pointer ready to encode the actual field.
            //
            // Note that the length must always be set to the length
            // required for regular second level encoding of the field,
            // regardless of whether regular encoding or delta encoding is
            // used.
            //
            ASSERT(numReps * pTableEntry->FieldEncodedLen < 256);
            *pNextFreeSpace =
                            (BYTE)(numReps * pTableEntry->FieldEncodedLen);
            pNextFreeSpace++;

            //
            // If we are encoding in delta coordinate mode and this field
            // is a coordinate...
            //
            if (useDeltaCoords &&
                       ((pTableEntry->FieldType & OE2_ETF_COORDINATES) != 0) )
            {
                //
                // Encode using delta coordinate encoding
                //
                OE2CopyToDeltaCoords((LPTSHR_INT8*)&pNextFreeSpace,
                                     pVariableField + sizeof(TSHR_UINT32),
                                     ((LPSTR)m_oe2Tx.pLastOrder)
                                           + pTableEntry->FieldPos
                                           + sizeof(TSHR_UINT32),
                                     pTableEntry->FieldUnencodedLen,
                                     pTableEntry->FieldSigned,
                                     numReps);
            }
            else
            {
                //
                // Use regular encoding
                //
                OE2EncodeField((LPBYTE)(pVariableField + sizeof(TSHR_UINT32)),
                               (LPBYTE*)&pNextFreeSpace,
                               pTableEntry->FieldUnencodedLen,
                               pTableEntry->FieldEncodedLen,
                               pTableEntry->FieldSigned,
                               numReps);

            }

            //
            // Keep data for comparison next time.
            //
            // Note that the variable fields of pLastOrder are not packed
            // (unlike the order which we are encoding), so we can use
            // pTableEntry->FieldPos to get the start of the field.
            //
            memcpy(((LPSTR)m_oe2Tx.pLastOrder) + pTableEntry->FieldPos,
                      pVariableField,
                      fieldLength + sizeof(TSHR_UINT32));
        }

        //
        // Move on to the next field in the order structure, remembering to
        // step.  Note that past the size field.  variable sized fields are
        // packed on the send side.  (ie increment pVariableField by
        // fieldLength not by pTableEntry->FieldLen).
        //
        pVariableField += fieldLength + sizeof(TSHR_UINT32);

        //
        // Make sure that we are at the next 4-byte boundary
        //
        if ((((UINT_PTR)pVariableField) % 4) != 0)
        {
            pVariableField += 4 - (((UINT_PTR)pVariableField) % 4);
        }

        thisFlag = thisFlag << 1;
        pTableEntry++;
    }

    //
    // record some stats:
    // Increment the count of order bytes of this type
    // Set the flags on for the fields which have been encoded
    //

    cbEncodedOrderSize = (UINT)(pNextFreeSpace - (LPSTR)pBuffer);

    TRACE_OUT(( "return %u flags %x,%x", cbEncodedOrderSize,
                                 (UINT)*pControlFlags, *pEncodingFlags));

encode_order_exit:
    //
    // "Insurance" check that we have not overwritten the end of the buffer.
    //
    if (cbEncodedOrderSize > cbBufferSize)
    {
        //
        // Oh dear!
        // We should never take this path - if we do, the code has gone
        // seriously wrong.
        //
        ERROR_OUT(( "End of buffer overwritten! enc(%d) buff(%d) type(%d)",
                     cbEncodedOrderSize,
                     cbBufferSize,
                     m_oe2Tx.LastOrderType));
    }

    //
    // Return the length of the encoded order
    //
    DebugExitDWORD(ASShare::OE2_EncodeOrder, cbEncodedOrderSize);
    return((TSHR_UINT16)cbEncodedOrderSize);
}



//
//
// OE2GetOrderType() - see oe2.h
//
//
BYTE  OE2GetOrderType(LPCOM_ORDER  pOrder)
{
    BYTE    type = 0xff;

    DebugEntry(OE2GetOrderType);

    TRACE_OUT(( "order type = %hx", TEXTFIELD(pOrder)->type));

    switch ( TEXTFIELD(pOrder)->type )
    {
        case ORD_DSTBLT_TYPE:
            type = OE2_DSTBLT_ORDER;
            break;

        case ORD_PATBLT_TYPE:
            type = OE2_PATBLT_ORDER;
            break;

        case ORD_SCRBLT_TYPE:
            type = OE2_SCRBLT_ORDER;
            break;

        case ORD_MEMBLT_TYPE:
            type = OE2_MEMBLT_ORDER;
            break;

        case ORD_MEM3BLT_TYPE:
            type = OE2_MEM3BLT_ORDER;
            break;

        case ORD_MEMBLT_R2_TYPE:
            type = OE2_MEMBLT_R2_ORDER;
            break;

        case ORD_MEM3BLT_R2_TYPE:
            type = OE2_MEM3BLT_R2_ORDER;
            break;

        case ORD_TEXTOUT_TYPE:
            type = OE2_TEXTOUT_ORDER;
            break;

        case ORD_EXTTEXTOUT_TYPE:
            type = OE2_EXTTEXTOUT_ORDER;
            break;

        case ORD_RECTANGLE_TYPE:
            type = OE2_RECTANGLE_ORDER;
            break;

        case ORD_LINETO_TYPE:
            type = OE2_LINETO_ORDER;
            break;

        case ORD_OPAQUERECT_TYPE:
            type = OE2_OPAQUERECT_ORDER;
            break;

        case ORD_SAVEBITMAP_TYPE:
            type = OE2_SAVEBITMAP_ORDER;
            break;

        case ORD_DESKSCROLL_TYPE:
            type = OE2_DESKSCROLL_ORDER;
            break;

        case ORD_POLYGON_TYPE:
            type = OE2_POLYGON_ORDER;
            break;

        case ORD_PIE_TYPE:
            type = OE2_PIE_ORDER;
            break;

        case ORD_ELLIPSE_TYPE:
            type = OE2_ELLIPSE_ORDER;
            break;

        case ORD_ARC_TYPE:
            type = OE2_ARC_ORDER;
            break;

        case ORD_CHORD_TYPE:
            type = OE2_CHORD_ORDER;
            break;

        case ORD_POLYBEZIER_TYPE:
            type = OE2_POLYBEZIER_ORDER;
            break;

        case ORD_ROUNDRECT_TYPE:
            type = OE2_ROUNDRECT_ORDER;
            break;

        default:
            ERROR_OUT(( "Unknown order type %x",
                        TEXTFIELD(pOrder)->type));

    }

    DebugExitDWORD(OE2GetOrderType, type);
    return(type);
}



//
// Given a pointer to 2 arrays, work out if the difference between every
// element at corresponding indices in the arrays can be represented by a
// delta (1 byte integer).
//
//   ARRAY1         - The first array
//   ARRAY2         - The second array
//   NUMELEMENTS    - The number of elements in the arrays
//   DELTASPOSSIBLE - The "return value".  Set to TRUE if all differences
//                    can be represented by deltas, FALSE if not.
//
#define CHECK_DELTA_ARRAY(ARRAY1, ARRAY2, NUMELEMENTS, DELTASPOSSIBLE)  \
{                                                                       \
    UINT  index;                                                      \
    int   delta;                                                      \
    (DELTASPOSSIBLE) = TRUE;                                            \
    for (index=0 ; index<(NUMELEMENTS) ; index++)                       \
    {                                                                   \
        delta = (ARRAY1)[index] - (ARRAY2)[index];                      \
        if (delta != (int)(TSHR_INT8)delta)                             \
        {                                                               \
            (DELTASPOSSIBLE) = FALSE;                                   \
            break;                                                      \
        }                                                               \
    }                                                                   \
}


//
//
// Name:      OE2CanUseDeltaCoords
//
// Purpose:   This function compares two arrays containing a number of
//            coordinate values.  If the difference between each
//            coordinate pair can be expressed as a byte sized delta
//            quantity then the function returns TRUE otherwise it returns
//            FALSE.
//
// Returns:   TRUE if delta coords can be used, FALSE otherwise
//
// Params:    IN pNewCoords  - Pointer to the new array
//            IN pOldCoords  - Pointer to the existing array
//            IN fieldLength - The size (in bytes) of each element in the
//                             array.
//            IN signedValue - TRUE of the elements in the arrays are
//                             signed values, FALSE otherwise.
//            IN numElements - The number of elements in the arrays.
//
//
BOOL  OE2CanUseDeltaCoords(void *  pNewCoords,
                                               void *  pOldCoords,
                                               UINT   fieldLength,
                                               BOOL   signedValue,
                                               UINT   numElements)
{
    LPTSHR_INT16    pNew16Signed   = (LPTSHR_INT16)pNewCoords;
    LPTSHR_INT32    pNew32Signed   = (LPTSHR_INT32)pNewCoords;
    LPTSHR_UINT16   pNew16Unsigned = (LPTSHR_UINT16)pNewCoords;
    LPUINT   pNew32Unsigned = (LPUINT)pNewCoords;
    LPTSHR_INT16    pOld16Signed   = (LPTSHR_INT16)pOldCoords;
    LPTSHR_INT32    pOld32Signed   = (LPTSHR_INT32)pOldCoords;
    LPTSHR_UINT16   pOld16Unsigned = (LPTSHR_UINT16)pOldCoords;
    LPUINT   pOld32Unsigned = (LPUINT)pOldCoords;
    BOOL      useDeltaCoords;

    DebugEntry(OE2CanUseDeltaCoords);

    switch (fieldLength)
    {
        case 2:
        {
            if (signedValue)
            {
                CHECK_DELTA_ARRAY(pNew16Signed,
                                  pOld16Signed,
                                  numElements,
                                  useDeltaCoords);
            }
            else
            {
                CHECK_DELTA_ARRAY(pNew16Unsigned,
                                  pOld16Unsigned,
                                  numElements,
                                  useDeltaCoords);
            }
        }
        break;

        case 4:
        {
            if (signedValue)
            {
                CHECK_DELTA_ARRAY(pNew32Signed,
                                  pOld32Signed,
                                  numElements,
                                  useDeltaCoords);
            }
            else
            {
                CHECK_DELTA_ARRAY(pNew32Unsigned,
                                  pOld32Unsigned,
                                  numElements,
                                  useDeltaCoords);
            }
        }
        break;

        default:
        {
            ERROR_OUT(( "Bad field length %d", fieldLength));
            useDeltaCoords = FALSE;
        }
        break;
    }

    DebugExitDWORD(OE2CanUseDeltaCoords, useDeltaCoords);
    return(useDeltaCoords);
}


//
// Given two arrays, fill in a delta array with each element holding
// ARRAY1[i] - ARRAY2[i]
//
//   DESTARRAY   - The delta array.  This is an array of TSHR_INT8s
//   ARRAY1      - The first array
//   ARRAY2      - The second array
//   NUMELEMENTS - The number of elements in the arrays
//
//
#define COPY_TO_DELTA_ARRAY(DESTARRAY, ARRAY1, ARRAY2, NUMELEMENTS)         \
{                                                                           \
    UINT index;                                                           \
    for (index=0 ; index<(NUMELEMENTS) ; index++)                           \
    {                                                                       \
        (DESTARRAY)[index] = (TSHR_INT8)((ARRAY1)[index] - (ARRAY2)[index]);   \
    }                                                                       \
}



//
//
// Name:      OE2CopyToDeltaCoords
//
// Purpose:   Copies an array of coordinate values to an array of delta
//            (byte sized) coordinate values relative to a reference array
//            of coordinate values.
//
// Returns:   Nothing
//
// Params:    IN/OUT ppDestination - Pointer to the start of the
//                                   destination delta array.  This is
//                                   updated to point to the byte following
//                                   the last delta on exit.
//            IN     pNewCoords    - Pointer to the new array
//            IN     pOldCoords    - Pointer to the reference array
//            IN     fieldLength   - The size (in bytes) of each element in
//                                   New/OldCoords arrays.
//            IN     signedValue   - TRUE of the elements in the coords
//                                   arrays are signed values, FALSE
//                                   otherwise.
//            IN     numElements   - The number of elements in the arrays.
//
// Operation: The caller should call OE2CanUseDeltaCoords() before calling
//            this function to ensure that the differences can be
//            encoded using delta coordingates.
//
//
void  OE2CopyToDeltaCoords(LPTSHR_INT8* ppDestination,
                                               void *  pNewCoords,
                                               void *  pOldCoords,
                                               UINT   fieldLength,
                                               BOOL   signedValue,
                                               UINT   numElements)
{

    LPTSHR_INT16    pNew16Signed   = (LPTSHR_INT16)pNewCoords;
    LPTSHR_INT32    pNew32Signed   = (LPTSHR_INT32)pNewCoords;
    LPTSHR_UINT16   pNew16Unsigned = (LPTSHR_UINT16)pNewCoords;
    LPUINT   pNew32Unsigned = (LPUINT)pNewCoords;
    LPTSHR_INT16    pOld16Signed   = (LPTSHR_INT16)pOldCoords;
    LPTSHR_INT32    pOld32Signed   = (LPTSHR_INT32)pOldCoords;
    LPTSHR_UINT16   pOld16Unsigned = (LPTSHR_UINT16)pOldCoords;
    LPUINT   pOld32Unsigned = (LPUINT)pOldCoords;

    DebugEntry(OE2CopyToDeltaCoords);

    switch (fieldLength)
    {
        case 2:
        {
            if (signedValue)
            {
                COPY_TO_DELTA_ARRAY(*ppDestination,
                                    pNew16Signed,
                                    pOld16Signed,
                                    numElements);
            }
            else
            {
                COPY_TO_DELTA_ARRAY(*ppDestination,
                                    pNew16Unsigned,
                                    pOld16Unsigned,
                                    numElements);
            }
        }
        break;

        case 4:
        {
            if (signedValue)
            {
                COPY_TO_DELTA_ARRAY(*ppDestination,
                                    pNew32Signed,
                                    pOld32Signed,
                                    numElements);
            }
            else
            {
                COPY_TO_DELTA_ARRAY(*ppDestination,
                                    pNew32Unsigned,
                                    pOld32Unsigned,
                                    numElements);
            }
        }
        break;

        default:
        {
            ERROR_OUT(( "Bad field length %d", fieldLength));
        }
        break;
    }

    //
    // Update the next free position in the destination buffer
    //
    *ppDestination += numElements;
    DebugExitVOID(OE2CopyToDeltaCoords);
}


//
// OE2EncodeBounds()
//
void  ASHost::OE2EncodeBounds
(
    LPBYTE *        ppNextFreeSpace,
    LPTSHR_RECT16   pRect
)
{
    LPBYTE          pFlags;

    DebugEntry(ASHost::OE2EncodeBounds);

    //
    // 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 = *ppNextFreeSpace;
    *pFlags = 0;
    (*ppNextFreeSpace)++;

    //
    // For each of the four coordinate values in the rectangle:  If the
    // coordinate has not changed then the encoding is null.  If the
    // coordinate can be encoded as a delta then do so and set the
    // appropriate flag.  Otherwise copy the coordinate as a 16bit value
    // and set the appropriate flag.
    //
    if (m_oe2Tx.LastBounds.left != pRect->left)
    {
        if (OE2CanUseDeltaCoords(&pRect->left,
                                 &m_oe2Tx.LastBounds.left,
                                 sizeof(pRect->left),
                                 TRUE,  // signed value
                                 1))
        {
            OE2CopyToDeltaCoords((LPTSHR_INT8*)ppNextFreeSpace,
                                 &pRect->left,
                                 &m_oe2Tx.LastBounds.left,
                                 sizeof(pRect->left),
                                 TRUE,  // signed value
                                 1);
            *pFlags |= OE2_BCF_DELTA_LEFT;
        }
        else
        {
            *((LPTSHR_UINT16)(*ppNextFreeSpace)) = pRect->left;
            *pFlags |= OE2_BCF_LEFT;
            (*ppNextFreeSpace) = (*ppNextFreeSpace) + sizeof(TSHR_UINT16);
        }
    }

    if (m_oe2Tx.LastBounds.top != pRect->top)
    {
        if (OE2CanUseDeltaCoords(&pRect->top,
                                 &m_oe2Tx.LastBounds.top,
                                 sizeof(pRect->top),
                                 TRUE,  // signed value
                                 1))
        {
            OE2CopyToDeltaCoords((LPTSHR_INT8*)ppNextFreeSpace,
                                 &pRect->top,
                                 &m_oe2Tx.LastBounds.top,
                                 sizeof(pRect->top),
                                 TRUE,  // signed value
                                 1);
            *pFlags |= OE2_BCF_DELTA_TOP;
        }
        else
        {
            *((LPTSHR_UINT16)(*ppNextFreeSpace)) = pRect->top;
            *pFlags |= OE2_BCF_TOP;
            (*ppNextFreeSpace) = (*ppNextFreeSpace) + sizeof(TSHR_UINT16);
        }
    }

    if (m_oe2Tx.LastBounds.right != pRect->right)
    {
        if (OE2CanUseDeltaCoords(&pRect->right,
                                 &m_oe2Tx.LastBounds.right,
                                 sizeof(pRect->right),
                                 TRUE,  // signed value
                                 1))
        {
            OE2CopyToDeltaCoords((LPTSHR_INT8*)ppNextFreeSpace,
                                 &pRect->right,
                                 &m_oe2Tx.LastBounds.right,
                                 sizeof(pRect->right),
                                 TRUE,  // signed value
                                 1);
            *pFlags |= OE2_BCF_DELTA_RIGHT;
        }
        else
        {
            *((LPTSHR_UINT16)(*ppNextFreeSpace)) = pRect->right;
            *pFlags |= OE2_BCF_RIGHT;
            (*ppNextFreeSpace) = (*ppNextFreeSpace) + sizeof(TSHR_UINT16);
        }
    }

    if (m_oe2Tx.LastBounds.bottom != pRect->bottom)
    {
        if (OE2CanUseDeltaCoords(&pRect->bottom,
                                 &m_oe2Tx.LastBounds.bottom,
                                 sizeof(pRect->bottom),
                                 TRUE,  // signed value
                                 1))
        {
            OE2CopyToDeltaCoords((LPTSHR_INT8*)ppNextFreeSpace,
                                 &pRect->bottom,
                                 &m_oe2Tx.LastBounds.bottom,
                                 sizeof(pRect->bottom),
                                 TRUE,  // signed value
                                 1);
            *pFlags |= OE2_BCF_DELTA_BOTTOM;
        }
        else
        {
            *((LPTSHR_UINT16)(*ppNextFreeSpace)) = pRect->bottom;
            *pFlags |= OE2_BCF_BOTTOM;
            (*ppNextFreeSpace) = (*ppNextFreeSpace) + sizeof(TSHR_UINT16);
        }
    }

    //
    // Copy the rectangle for reference with the next encoding.
    //
    m_oe2Tx.LastBounds = *pRect;

    DebugExitVOID(ASHost::OE2EncodeBounds);
}



//
// OE2_UseFont()
//
BOOL  ASHost::OE2_UseFont
(
    LPSTR           pName,
    TSHR_UINT16     facelength,
    TSHR_UINT16     CodePage,
    TSHR_UINT16     MaxHeight,
    TSHR_UINT16     Height,
    TSHR_UINT16     Width,
    TSHR_UINT16     Weight,
    TSHR_UINT16     flags
)
{
    BOOL      rc = TRUE;

    DebugEntry(ASHost::OE2_UseFont);

    if ((!m_oe2Tx.LastHFONT) ||
        (m_oe2Tx.LastFontFaceLen != facelength) ||
        (memcmp(m_oe2Tx.LastFaceName, pName, facelength)) ||
        (m_oe2Tx.LastCodePage   != CodePage) ||
        (m_oe2Tx.LastFontHeight != Height ) ||
        (m_oe2Tx.LastFontWidth  != Width  ) ||
        (m_oe2Tx.LastFontWeight != Weight ) ||
        (m_oe2Tx.LastFontFlags  != flags  ))
    {
        memcpy(m_oe2Tx.LastFaceName, pName, facelength);
        m_oe2Tx.LastFaceName[facelength] = '\0';
        m_oe2Tx.LastFontFaceLen          = facelength;
        m_oe2Tx.LastCodePage   = CodePage;
        m_oe2Tx.LastFontHeight = Height;
        m_oe2Tx.LastFontWidth  = Width;
        m_oe2Tx.LastFontWeight = Weight;
        m_oe2Tx.LastFontFlags  = flags;

        rc = m_pShare->USR_UseFont(m_usrWorkDC,
                         &m_oe2Tx.LastHFONT,
                         &m_oe2Tx.LastFontMetrics,
                         (LPSTR)m_oe2Tx.LastFaceName,
                         CodePage,
                         MaxHeight,
                         Height,
                         Width,
                         Weight,
                         flags);
    }

    DebugExitBOOL(ASHost::OE2_UseFont, rc);
    return(rc);
}



//
// 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];   \
    }                                                       \
}


//
// OE2EncodeField - see oe2.h
//
void  OE2EncodeField(void *    pSrc,
                    LPBYTE*  ppDest,
                                         UINT     srcFieldLength,
                                         UINT     destFieldLength,
                                         BOOL     signedValue,
                                         UINT     numElements)
{
    LPTSHR_UINT8    pSrc8           = (LPTSHR_UINT8)pSrc;
    LPTSHR_INT16    pSrc16Signed    = (LPTSHR_INT16)pSrc;
    LPTSHR_INT32    pSrc32Signed    = (LPTSHR_INT32)pSrc;
    LPTSHR_INT8     pDest8Signed    = (LPTSHR_INT8)*ppDest;
    LPTSHR_INT16_UA pDest16Signed   = (LPTSHR_INT16_UA)*ppDest;

    //
    // Note that the source fields may not be aligned correctly, so we use
    // unaligned pointers.  The destination is aligned correctly.
    //

    DebugEntry(OE2EncodeField);

    //
    // We can ignore signed values since we only ever truncate the data.
    // Consider the case where we have a 16 bit integer that we want to
    // convert to 8 bits.  We know our values are permissable within the
    // lower integer size (ie.  we know the unsigned value will be less
    // than 256 of that a signed value will be -128 >= value >= 127), so we
    // just need to make sure that we have the right high bit set.
    //
    // But this must be the case for a 16-bit equivalent of an 8-bit
    // number.  No problems - just take the truncated integer.
    //
    //
    // 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 (srcFieldLength < destFieldLength)
    {
        ERROR_OUT(( "Source field length %d is smaller than destination %d",
                     srcFieldLength,
                     destFieldLength));
        DC_QUIT;
    }

    //
    // If the source and destination field lengths are the same, we can
    // just do a copy (no type conversion required).
    //
    if (srcFieldLength == destFieldLength)
    {
        memcpy(*ppDest, pSrc, destFieldLength * numElements);
    }
    else
    {
        //
        // We know that srcFieldLength must be greater than destFieldLength
        // because of our checks above.  So there are only three
        // conversions to consider:
        //
        //   16 bit ->  8 bit
        //   32 bit ->  8 bit
        //   32 bit -> 16 bit
        //
        // We can ignore the sign as all we are ever doing is truncating
        // the integer.
        //
        if ((srcFieldLength == 4) && (destFieldLength == 1))
        {
            CONVERT_ARRAY(pDest8Signed,
                          pSrc32Signed,
                          TSHR_INT8,
                          numElements);
        }
        else if ((srcFieldLength == 4) && (destFieldLength == 2))
        {
            CONVERT_ARRAY(pDest16Signed,
                          pSrc32Signed,
                          TSHR_INT16,
                          numElements);
        }
        else if ((srcFieldLength == 2) && (destFieldLength == 1))
        {
            CONVERT_ARRAY(pDest8Signed,
                          pSrc16Signed,
                          TSHR_INT8,
                          numElements);
        }
        else
        {
            ERROR_OUT(( "Bad conversion, dest length = %d, src length = %d",
                         destFieldLength,
                         srcFieldLength));
        }
    }

DC_EXIT_POINT:
    *ppDest += destFieldLength * numElements;
    DebugExitVOID(OE2EncodeField);
}