#include "precomp.h"


//
// RBC.CPP
// Received Bitmap Cache
//
// Copyright(c) Microsoft 1997-
//

#define MLZ_FILE_ZONE  ZONE_CORE



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

    DebugEntry(ASShare::RBC_ViewStarting);

    ValidatePerson(pasPerson);

    //
    // Allocate the INCOMING cache data for this host.
    //
    pasPerson->prbcHost = new RBC_HOST_INFO;
    if (!pasPerson->prbcHost)
    {
        ERROR_OUT(( "Failed to get memory for prbcHost info"));
        DC_QUIT;
    }
    ZeroMemory(pasPerson->prbcHost, sizeof(*(pasPerson->prbcHost)));
    SET_STAMP(pasPerson->prbcHost, RBCHOST);

    TRACE_OUT(( "Allocated RBC root for host [%d] at 0x%08x",
        pasPerson->mcsID, pasPerson->prbcHost));

    //
    // Create the bitmap caches for the sender
    //

    // SMALL
    if (!BMCAllocateCacheData(pasPerson->cpcCaps.bitmaps.sender.capsSmallCacheNumEntries,
            pasPerson->cpcCaps.bitmaps.sender.capsSmallCacheCellSize,
            ID_SMALL_BMP_CACHE,
            &(pasPerson->prbcHost->bitmapCache[ID_SMALL_BMP_CACHE])))
    {
        DC_QUIT;
    }

    // MEDIUM
    if (!BMCAllocateCacheData(pasPerson->cpcCaps.bitmaps.sender.capsMediumCacheNumEntries,
            pasPerson->cpcCaps.bitmaps.sender.capsMediumCacheCellSize,
            ID_MEDIUM_BMP_CACHE,
            &(pasPerson->prbcHost->bitmapCache[ID_MEDIUM_BMP_CACHE])))
    {
        DC_QUIT;
    }

    // LARGE
    if (!BMCAllocateCacheData(pasPerson->cpcCaps.bitmaps.sender.capsLargeCacheNumEntries,
            pasPerson->cpcCaps.bitmaps.sender.capsLargeCacheCellSize,
            ID_LARGE_BMP_CACHE,
            &(pasPerson->prbcHost->bitmapCache[ID_LARGE_BMP_CACHE])))
    {
        DC_QUIT;
    }

    //
    // The host can join the share.
    //
    rc = TRUE;

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


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

    ValidatePerson(pasPerson);

    RBCFreeIncoming(pasPerson);

    DebugExitVOID(ASShare::RBC_ViewEnded);
}


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

    ValidatePerson(pasPerson);

    // This should be gone!
    ASSERT(pasPerson->prbcHost == NULL);

    DebugExitVOID(ASShare::RBC_PartyLeftShare);
}


//
// RBCFreeIncoming()
// Frees the party RBC incoming structures.  This happens
//      * For 3.0 nodes when they stop hosting
//      * For 2.x nodes when leave the share
//
void ASShare::RBCFreeIncoming(ASPerson * pasPerson)
{
    DebugEntry(ASShare::RBCFreeIncoming);

    //
    // Free this host's cache bitmaps.
    //
    if (pasPerson->prbcHost != NULL)
    {
        UINT  i;

        //
        // Delete all of this host's cache bitmaps.
        //
        for (i = 0; i < NUM_BMP_CACHES; i++)
        {
            BMCFreeCacheData(&(pasPerson->prbcHost->bitmapCache[i]));
        }

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

    DebugExitVOID(ASShare::RBCFreeIncoming);
}


//
// RBC_ProcessCacheOrder(..)
//
void  ASShare::RBC_ProcessCacheOrder
(
    ASPerson *              pasPerson,
    LPCOM_ORDER_UA          pOrder
)
{
    PBMC_ORDER_HDR               pBmcOrderHdr;
    PBMC_COLOR_TABLE_ORDER_UA    pColorOrder;
    PBMC_BITMAP_BITS_ORDER_R2_UA pBitsOrderR2;
    BOOL                    fCompressed = FALSE;
    UINT                    cxFixedBitmapWidth;
    UINT                    iCacheEntry;
    LPBYTE                  pBitmapBits;
    UINT                    cbBitmapBits;

    DebugEntry(ASShare::RBC_ProcessCacheOrder);

    ValidatePerson(pasPerson);

    //
    // The rectangle is not included in the header for private order data
    // (see SBC_CopyPrivateOrderData) so we must take this into account
    // when working out the address of the order data.
    //
    pBmcOrderHdr = (PBMC_ORDER_HDR)
                   (pOrder->abOrderData - sizeof(pOrder->OrderHeader.rcsDst));

    switch (pBmcOrderHdr->bmcPacketType)
    {
        case BMC_PT_COLOR_TABLE:
            //
            // This is a new color table.  Simply cache the RGB values for
            // use when we come to process a memblt order
            // For backlevel calls the color table is always stored at
            // index 0 because the index field in the order reuses a
            // zero initialized "padding" field in the old structure.
            //
            TRACE_OUT(("Person [%d] Caching color table", pasPerson->mcsID));
            pColorOrder = (PBMC_COLOR_TABLE_ORDER_UA)pBmcOrderHdr;

            PM_CacheRxColorTable(pasPerson, pColorOrder->index,
                EXTRACT_TSHR_UINT16_UA(&(pColorOrder->colorTableSize)),
                                 (LPTSHR_RGBQUAD)&pColorOrder->data[0]);
            break;

        case BMC_PT_BITMAP_BITS_COMPRESSED:
            fCompressed = TRUE;
            TRACE_OUT(( "Compressed BMP"));
        case BMC_PT_BITMAP_BITS_UNCOMPRESSED:
            //
            // This is some cached bitmap data.  We have to store it in the
            // specified slot in the specified cache.
            //

            //
            // The width of the bitmaps we use are actually fixed as
            // multiples of 16 pels wide.  Work out the width that
            // corresponds to the sub-bitmap width of data we are caching.
            //
            pBitsOrderR2 = (PBMC_BITMAP_BITS_ORDER_R2_UA)pBmcOrderHdr;

            cbBitmapBits = EXTRACT_TSHR_UINT16_UA(
                                        &(pBitsOrderR2->header.cbBitmapBits));

            cxFixedBitmapWidth =
                          ((pBitsOrderR2->header.cxSubBitmapWidth +15)/16)*16;

            //
            // The location of cache entry field depends on the R1/R2
            // protocol
            //
            iCacheEntry = EXTRACT_TSHR_UINT16_UA(&(pBitsOrderR2->iCacheEntryR2));
            pBitmapBits = pBitsOrderR2->data;

            TRACE_OUT(("Person [%d] Rx bmp: id(%d) entry(%d) size(%dx%d) " \
                        "fixed(%d) bpp(%d) bytes(%d) compressed(%d)",
                    pasPerson->mcsID,
                    pBitsOrderR2->header.cacheID,
                    iCacheEntry,
                    pBitsOrderR2->header.cxSubBitmapWidth,
                    pBitsOrderR2->header.cySubBitmapHeight,
                    cxFixedBitmapWidth,
                    pBitsOrderR2->header.bpp,
                    cbBitmapBits,
                    fCompressed));

            //
            // Pass the BMC data to the caching code.  When calculating the
            // pointer to the bitmap bits remember that we did not send the
            // pBitmapBits field of the BMC_BITMAP_BITS_ORDER_Rx structure
            // (see SBC_CopyPrivateOrderData).
            //
            RBCStoreBitsInCacheBitmap(pasPerson,
                             pBitsOrderR2->header.cacheID,
                             iCacheEntry,
                             pBitsOrderR2->header.cxSubBitmapWidth,
                             cxFixedBitmapWidth,
                             pBitsOrderR2->header.cySubBitmapHeight,
                             pBitsOrderR2->header.bpp,
                             pBitmapBits,
                             cbBitmapBits,
                             fCompressed);
            break;

        default:
            ERROR_OUT(( "[%u]Invalid packet type(%d)",
                       pasPerson,
                       (UINT)pBmcOrderHdr->bmcPacketType));
            break;
    }

    DebugExitVOID(ASShare::RBC_ProcessCacheOrder);
}


//
// RBC_MapCacheIDToBitmapHandle(..)
//
HBITMAP  ASShare::RBC_MapCacheIDToBitmapHandle
(
    ASPerson *          pasPerson,
    UINT                cache,
    UINT                cacheEntry,
    UINT                colorIndex
)
{
    PBMC_DIB_CACHE      pDIBCache;
    PBMC_DIB_ENTRY      pDIBEntry;
    BITMAPINFO_ours     bitmapInfo;
    UINT                cColors;
    HBITMAP             hWorkBitmap = NULL;
    HPALETTE            hpalOldDIB = NULL;
    LPBYTE              pBits;
    UINT                cacheOffset;

    DebugEntry(ASShare::RBC_MapCacheIDToBitmapHandle);

    ValidateView(pasPerson);

    //
    // Check that the supplied cache ID is valid.
    //
    if (cache >= NUM_BMP_CACHES)
    {
        ERROR_OUT(( "[%u]Invalid cache ID (%d)", pasPerson, cache));
        cache = 0;
    }

    //
    // Get a pointer to the bitmap data
    //
    // Note that there are two indexes floating around.  From the host's
    // perspective this index is a Cache Handler token and it must be
    // translated in order to address the associated data.  However we
    // use it as the direct index into our receive cache and so the
    // slots used on host and remote will be diferent.
    //
    // There is no reason why the slots should be the same.  This is just
    // to warn you that if you try correlating cache offsets between
    // host and remote you will get confused as soon as the cache fills
    // up and entries are reallocated in different positions.
    //
    //
    pDIBCache = &(pasPerson->prbcHost->bitmapCache[cache]);
    TRACE_OUT(( "Local person [%d] cache id %d pointer %lx",
        pasPerson->mcsID, cache, pDIBCache));
    cacheOffset = cacheEntry * pDIBCache->cSize;
    pDIBEntry = (PBMC_DIB_ENTRY)(pDIBCache->data + cacheOffset);

    TRACE_OUT(( "Bits for index %u are at offset %ld, pointer 0x%08x",
        cacheEntry, (cacheEntry * pDIBCache->cSize), pDIBEntry));

    //
    // Set up the BitmapInfo structure.
    //
    USR_InitDIBitmapHeader((BITMAPINFOHEADER *)&bitmapInfo, pDIBEntry->bpp);
    bitmapInfo.bmiHeader.biWidth  = pDIBEntry->cxFixed;
    bitmapInfo.bmiHeader.biHeight = pDIBEntry->cy;

    //
    // Copy the Rx color table into the bitmap header.
    //
    if ( (pDIBEntry->bpp == 1) ||
         (pDIBEntry->bpp == 4) ||
         (pDIBEntry->bpp == 8) )
    {
        cColors = COLORS_FOR_BPP(pDIBEntry->bpp);

        PM_GetColorTable( pasPerson,
                          colorIndex,
                          &cColors,
                          (LPTSHR_RGBQUAD)(&bitmapInfo.bmiColors) );
        TRACE_OUT(( "Got %u colors from table",cColors));
        bitmapInfo.bmiHeader.biClrUsed = cColors;
    }
    else if (pDIBEntry->bpp == 24)
    {
        ASSERT(colorIndex == COLORCACHEINDEX_NONE);
    }
    else
    {
        ERROR_OUT(("RBC: Unexpected bpp %d from [%d]", pDIBEntry->bpp, pasPerson->mcsID));
        DC_QUIT;
    }

    //
    // Select which fixed width bitmap we are going to use to store the
    // incoming DIB bits.
    //
    switch (pDIBEntry->cxFixed)
    {
        case 16:
            hWorkBitmap = m_usrBmp16;
            break;

        case 32:
            hWorkBitmap = m_usrBmp32;
            break;

        case 48:
            hWorkBitmap = m_usrBmp48;
            break;

        case 64:
            hWorkBitmap = m_usrBmp64;
            break;

        case 80:
            hWorkBitmap = m_usrBmp80;
            break;

        case 96:
            hWorkBitmap = m_usrBmp96;
            break;

        case 112:
            hWorkBitmap = m_usrBmp112;
            break;

        case 128:
            hWorkBitmap = m_usrBmp128;
            break;

        case 256:
            hWorkBitmap = m_usrBmp256;
            break;

        default:
            ERROR_OUT(("RBC_MapCacheIDToBitmapHandle: invalid size from [%d]",
                pDIBEntry->cxFixed, pasPerson->mcsID));
            hWorkBitmap = m_usrBmp256;
            break;
    }

    ASSERT(hWorkBitmap != NULL);


    //
    // If the cached bitmap bits are compressed, we first have to
    // decompress them.
    //
    if (pDIBEntry->bCompressed)
    {
        ASSERT(pDIBEntry->bpp <= 8);

        //
        // Use the decompression buffer to decompress the bitmap data.
        //
        if (!BD_DecompressBitmap(pDIBEntry->bits, m_usrPBitmapBuffer,
                                 pDIBEntry->cCompressed,
                                 pDIBEntry->cxFixed,
                                 pDIBEntry->cy,
                                 pDIBEntry->bpp))
        {
             ERROR_OUT((
                      "Failed to decompress bitmap pBits(%lx)"
                      " pBuf(%lx) cb(%x) cx(%d) cy(%d) bpp(%d)",
                      pDIBEntry->bits,
                      m_usrPBitmapBuffer,
                      pDIBEntry->cCompressed,
                      pDIBEntry->cxFixed,
                      pDIBEntry->cy,
                      pDIBEntry->bpp));
             DC_QUIT;
        }

        pBits = m_usrPBitmapBuffer;
    }
    else
    {
        //
        // For uncompressed data just use direct from the cache
        //
        TRACE_OUT(( "Bitmap bits are uncompressed"));
        pBits = pDIBEntry->bits;
    }


    //
    // Set the bits into the bitmap we are about to return to the caller
    //
    hpalOldDIB = SelectPalette(pasPerson->m_pView->m_usrWorkDC,
        pasPerson->pmPalette, FALSE);
    RealizePalette(pasPerson->m_pView->m_usrWorkDC);

    if (!SetDIBits(pasPerson->m_pView->m_usrWorkDC,
                      hWorkBitmap,
                      0,
                      pDIBEntry->cy,
                      pBits,
                      (BITMAPINFO *)&bitmapInfo,
                      DIB_RGB_COLORS))
    {
        ERROR_OUT(("SetDIBits failed in RBC_MapCacheIDToBitmapHandle"));
    }

    SelectPalette(pasPerson->m_pView->m_usrWorkDC, hpalOldDIB, FALSE );

    TRACE_OUT(( "Returning bitmap for person [%d] cache %u index %u color %u",
        pasPerson->mcsID, cache, cacheEntry, colorIndex));


DC_EXIT_POINT:
    DebugExitVOID(ASShare::RBC_MapCacheIDToBitmapHandle);
    return(hWorkBitmap);
}






//
// FUNCTION: RBCStoreBitsInCacheBitmap(..)
//
// DESCRIPTION:
//
// Stores received bitmap bits into one of the receiver's cache bitmaps.
//
// PARAMETERS:
//
// pasPerson - pasPerson of host the bits came from.
//
// cache - the id of the cache bitmap to store the bits in.
//
// iCacheEntry - the cache entry number (index).
//
// cxSubBitmapWidth - the width in pels of the actual sub-bitmap (ie.
// excluding padding)
//
// cxFixedWidth - the fixed width in pels of the supplied bits (ie.
// including padding)
//
// cySubBitmapHeight - the height in pels of the sub-bitmap.
//
// pBitmapBits - a pointer to the actual bitmap bits. These may or may
// not be compressed (determined by the value of the fCompressed
// flag).
//
// cbBitmapBits - the size of the bitmap bits pointed to by pBitmapBits.
//
// fCompressed - a flag specifying whether the supplied bitmap
// bits are compressed.
//
// RETURNS:
//
// Nothing.
//
//
void  ASShare::RBCStoreBitsInCacheBitmap
(
    ASPerson *          pasPerson,
    UINT                cache,
    UINT                iCacheEntry,
    UINT                cxSubBitmapWidth,
    UINT                cxFixedWidth,
    UINT                cySubBitmapHeight,
    UINT                bpp,
    LPBYTE              pBitmapBits,
    UINT                cbBitmapBits,
    BOOL                fCompressed
)
{
    PBMC_DIB_ENTRY      pDIBEntry;

    DebugEntry(ASShare::RBCStoreBitsInCacheBitmap);

    ValidatePerson(pasPerson);

    //
    // Do some error checking.
    //
    if (cache >= NUM_BMP_CACHES)
    {
        ERROR_OUT(("Invalid cache ID %d from [%d]", cache, pasPerson->mcsID));
        DC_QUIT;
    }

    //
    // Now store the bits in the cache
    // The cache is a huge chunk of memory comprising cache slots of cSize
    // bytes each.  cSize is rounded to a power of 2 to ensure the array
    // spans segment boundaries cleanly for segmented architecture OSs.
    //
    pDIBEntry = (PBMC_DIB_ENTRY)
        (((LPBYTE)(pasPerson->prbcHost->bitmapCache[cache].data) +
         (iCacheEntry * pasPerson->prbcHost->bitmapCache[cache].cSize)));
    TRACE_OUT(( "Selected cache entry 0x%08x",pDIBEntry));

    pDIBEntry->inUse       = TRUE;
    pDIBEntry->cx          = (TSHR_UINT16)cxSubBitmapWidth;
    pDIBEntry->cxFixed     = (TSHR_UINT16)cxFixedWidth;
    pDIBEntry->cy          = (TSHR_UINT16)cySubBitmapHeight;
    pDIBEntry->bpp         = (TSHR_UINT16)bpp;
    pDIBEntry->bCompressed = (fCompressed != FALSE);
    pDIBEntry->cCompressed = cbBitmapBits;

    //
    // Now copy the bits into the cache entry
    //
    memcpy(pDIBEntry->bits, pBitmapBits, cbBitmapBits);

    //
    // THIS FIELD IS NEVER ACCESSED.
    //
    pDIBEntry->cBits = BYTES_IN_BITMAP(cxFixedWidth, cySubBitmapHeight,
        pDIBEntry->bpp);

DC_EXIT_POINT:
    DebugExitVOID(ASShare::RBCStoreBitsInCacheBitmap);
}




//
// BMCAllocateCacheData()
//
// DESCRIPTION:
//
// Allocates memory for a bitmap cache
//
// PARAMETERS:
//
// cellSize
//
// RETURNS:
//
// Area needed
//
//
BOOL  BMCAllocateCacheData
(
    UINT            numEntries,
    UINT            cellSize,
    UINT            cacheID,
    PBMC_DIB_CACHE  pCache
)
{
    BOOL            rc = TRUE;
    UINT            memoryNeeded;
    UINT            workSize;
    PBMC_DIB_ENTRY  pCacheEntry;
    UINT            i;

    DebugEntry(BMCAllocateCacheData);

    //
    // First we must free up any data, if it has been allocated
    //
    BMCFreeCacheData(pCache);

    //
    // For 2.x compat, we have SEND caps of 1 entry, 1 byte since 2.x
    // remotes fail for zero entries.  But we don't want a small cache
    // at all, and for W95 nodes that don't have a cache at all, we don't
    // want viewers to alloc memory which will never be used.
    //
    if ((cellSize > 1) && (numEntries > 1))
    {
        //
        // Calculate the cell area
        //
        workSize        = cellSize + sizeof(BMC_DIB_ENTRY) - 1;
        memoryNeeded    = numEntries * workSize;

        TRACE_OUT(("Need 0x%08x bytes for cache %d, %d cells of size 0x%08x",
            memoryNeeded, cacheID, numEntries, cellSize));

        //
        // Malloc the huge space
        //
        pCache->data = new BYTE[memoryNeeded];
        if (pCache->data == NULL)
        {
            ERROR_OUT(( "Failed to alloc bitmap cache %d", cacheID));
            rc = FALSE;
            DC_QUIT;
        }

        pCache->cCellSize   = cellSize;
        pCache->cEntries    = numEntries;
        pCache->cSize       = workSize;
        pCache->freeEntry   = NULL;
        pCacheEntry         = (PBMC_DIB_ENTRY)(pCache->data);

        for (i = 0; i < numEntries; i++)
        {
            pCacheEntry->inUse = FALSE;
            pCacheEntry = (PBMC_DIB_ENTRY)(((LPBYTE)pCacheEntry) + workSize);
        }

        TRACE_OUT(( "Allocated cache %d size %d, pointer 0x%08x stored at 0x%08x",
                     cacheID,
                     memoryNeeded,
                     pCache->data,
                     &pCache->data));
    }

DC_EXIT_POINT:
    DebugExitBOOL(BMCAllocateCacheData, rc);
    return(rc);
}



//
// FUNCTION: BMCFreeCacheData()
//
// DESCRIPTION:
//
// Deletes selected cache's memory
//
// PARAMETERS:
//
// cacheID - id of cache for free
// pCache  - pointer to memory to be freed
//
//
// RETURNS:
//
// Nothing.
//
//
void  BMCFreeCacheData(PBMC_DIB_CACHE pCache)
{
    DebugEntry(BMCFreeCacheData);

    if (pCache->data)
    {
        delete[] pCache->data;
        pCache->data = NULL;
    }

    pCache->cCellSize   = 0;
    pCache->cEntries    = 0;

    DebugExitVOID(BMCFreeCacheData);
}