/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: pxcjpgff.cpp,v 1.1.26.1 2004/07/09 01:51:18 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

// include
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxcom.h"
#include "hxplugn.h"
#include "ihxpckts.h"
#include "hxver.h"
#include "hxcomm.h"

// pnmisc
#include "baseobj.h"
#include "unkimp.h"

// pncont
#include "hxslist.h"
#include "hxstring.h"
#include "hxmap.h"
#include "carray.h"

// pxcomlib
#include "pxrndcod.h"
#include "pxffmcod.h"
#include "nestbuff.h"
#include "gstring.h"
#include "pxutil.h"
#include "pxmapmgr.h"
#include "pxparse.h"

// pxjpeg2
#include "pxcjpgff.h"
#include "rpjpgdll.ver"

// pndebug
#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE     
static char HX_THIS_FILE[] = __FILE__;
#endif

#ifdef _AIX
#include "dllpath.h"
ENABLE_MULTILOAD_DLLACCESS_PATHS(Pxcjpeg2);
#endif

const char*  CRealPixJPEGFileFormatCodec::m_pszDescription      = "Helix RealPix JPEG File Format Codec Plugin";
const char*  CRealPixJPEGFileFormatCodec::m_pszCopyright        = HXVER_COPYRIGHT;
const char*  CRealPixJPEGFileFormatCodec::m_pszMoreInfoURL      = HXVER_MOREINFO;
const char*  CRealPixJPEGFileFormatCodec::m_ppszFileExtension[] = {"jpg", "jpeg", "jpe", "jfif", "fpx", NULL};
const char*  CRealPixJPEGFileFormatCodec::m_ppszFileMimeType[]  = {"image/jpeg", "image/pjpeg", NULL};
const char*  CRealPixJPEGFileFormatCodec::m_pszStreamMimeType   = "image/vndr.rn-realpix.jpeg";
const UINT32 CRealPixJPEGFileFormatCodec::m_ulStreamVersion     = HX_ENCODE_PROD_VERSION(0, 0, 0, 0);

CRealPixJPEGFileFormatCodec::CRealPixJPEGFileFormatCodec()
{
    m_lRefCount           = 0;
    m_pContext            = NULL;
    m_pCommonClassFactory = NULL;
    m_pMapManager         = NULL;
}

CRealPixJPEGFileFormatCodec::~CRealPixJPEGFileFormatCodec()
{
    ReleaseAllSessions();
    HX_RELEASE(m_pContext);
    HX_RELEASE(m_pCommonClassFactory);
    HX_RELEASE(m_pMapManager);
}

STDMETHODIMP CRealPixJPEGFileFormatCodec::QueryInterface(REFIID riid, void** ppvObj)
{
    HX_RESULT retVal = HXR_OK;

    if (ppvObj)
    {
        // Set default
        *ppvObj = NULL;
        // Check for IID type
        if (IsEqualIID(riid, IID_IUnknown))
        {
            AddRef();
            *ppvObj = (IUnknown*) (IHXPlugin*) this;
        }
        else if (IsEqualIID(riid, IID_IHXPlugin))
        {
            AddRef();
            *ppvObj = (IHXPlugin*) this;
        }
        else if (IsEqualIID(riid, IID_IHXRealPixFileFormatCodec))
        {
            AddRef();
            *ppvObj = (IHXRealPixFileFormatCodec*) this;
        }
        else
        {
            retVal = HXR_NOINTERFACE;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

STDMETHODIMP_(UINT32) CRealPixJPEGFileFormatCodec::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}


STDMETHODIMP_(UINT32) CRealPixJPEGFileFormatCodec::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;

    return 0;
}

STDMETHODIMP CRealPixJPEGFileFormatCodec::GetPluginInfo(REF(BOOL)        bMultipleLoad,
                                                        REF(const char*) pDescription,
                                                        REF(const char*) pCopyright,
                                                        REF(const char*) pMoreInfoURL,
                                                        REF(ULONG32)     ulVersionNumber)
{
    bMultipleLoad   = TRUE;
    pDescription    = m_pszDescription;
    pCopyright      = m_pszCopyright;
    pMoreInfoURL    = m_pszMoreInfoURL;
    ulVersionNumber = TARVER_ULONG32_VERSION;

    return HXR_OK;
}

STDMETHODIMP CRealPixJPEGFileFormatCodec::InitPlugin(IUnknown* pContext)
{
    HX_RESULT retVal = HXR_OK;

    if (pContext)
    {
        // Save a copy of the context
        HX_RELEASE(m_pContext);
        m_pContext = pContext;
        m_pContext->AddRef();

        // Get an IHXCommonClassFactory interface
        HX_RELEASE(m_pCommonClassFactory);
        retVal = m_pContext->QueryInterface(IID_IHXCommonClassFactory,
                                            (void**) &m_pCommonClassFactory);
        if (SUCCEEDED(retVal))
        {
            // Create a map manager
            HX_RELEASE(m_pMapManager);
            m_pMapManager = new PXMapManager();
            if (m_pMapManager)
            {
                m_pMapManager->AddRef();
            }
            else
            {
                retVal = HXR_OUTOFMEMORY;
            }
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

STDMETHODIMP CRealPixJPEGFileFormatCodec::GetFileFormatCodecInfo(REF(const char**) rppszFileExtension,
                                                                 REF(const char**) rppszFileMimeType,
                                                                 REF(const char*)  rpszStreamMimeType,
                                                                 REF(UINT32)       rulStreamVersion,
                                                                 REF(UINT32)       rulMaxPerImageOpaque,
                                                                 REF(UINT32)       rulMaxPerPacketOpaque)
{
    rppszFileExtension    = m_ppszFileExtension;
    rppszFileMimeType     = m_ppszFileMimeType;
    rpszStreamMimeType    = m_pszStreamMimeType;
    rulStreamVersion      = m_ulStreamVersion;
    rulMaxPerImageOpaque  = 0;
    rulMaxPerPacketOpaque = 12;

    return HXR_OK;
}

STDMETHODIMP_(BOOL) CRealPixJPEGFileFormatCodec::ValidInputData(IHXBuffer* pBuffer)
{
    BOOL bRet = FALSE;

    if (pBuffer)
    {
        BYTE* pBuf = pBuffer->GetBuffer();
        if (pBuf)
        {
            if (pBuf[0]  == 0xFF && pBuf[1] == kMarkerSOI  &&
                pBuf[2]  == 0xFF && pBuf[3] == kMarkerAPP0 &&
                pBuf[4]  == 0x00 && pBuf[5] == 0x10        &&
                pBuf[6]  == 'J'  && pBuf[7] == 'F'         &&
                pBuf[8]  == 'I'  && pBuf[9] == 'F'         &&
                pBuf[10] == 0x00)
            {
                bRet = TRUE;
            }
        }
    }

    return bRet;
}

STDMETHODIMP CRealPixJPEGFileFormatCodec::ParseImage(IHXBuffer*      pBuffer,
                                                     REF(UINT32)      rulNumPackets,
                                                     REF(IHXValues*) rpParam,
                                                     REF(UINT32)      rulSessionHandle)
{
    HX_RESULT retVal = HXR_OK;

    if (pBuffer)
    {
        // Compute the maximum number of packets
        UINT32 ulMaxNumPackets = (pBuffer->GetSize() + kMinPacketSize - 1) / kMinPacketSize;

        // Create a PXParseSession object
        PXParseSession* pSession = new PXParseSession();
        if (pSession)
        {
            // AddRef the object
            pSession->AddRef();
            // Init the size
            retVal = pSession->InitSize(ulMaxNumPackets);
            if (SUCCEEDED(retVal))
            {
                // Parse the image, putting buffers into the session object
                HXxSize rImageDim;
                retVal = ParseBuffer(pBuffer, pSession, rImageDim);
                if (SUCCEEDED(retVal))
                {
                    // Now we can reduce our size to the number of packets we actually have
                    retVal = pSession->SetSize(pSession->GetNumPackets());
                    if (SUCCEEDED(retVal))
                    {
                        // Add the session to the map
                        retVal = m_pMapManager->AddEntry((void*) pSession, rulSessionHandle);
                        if (SUCCEEDED(retVal))
                        {
                            // AddRef the session since it's in the map
                            pSession->AddRef();
                            // Create an IHXValues object
                            IHXValues* pParam = NULL;
                            retVal             = m_pCommonClassFactory->CreateInstance(CLSID_IHXValues,
                                                                                       (void**) &pParam);
                            if (SUCCEEDED(retVal))
                            {
                                // Set properties in the IHXValues
                                pParam->SetPropertyULONG32("MaxPacketSize",    pSession->GetMaxPacketSize());
                                pParam->SetPropertyULONG32("MinPacketSize",    pSession->GetMinPacketSize());
                                pParam->SetPropertyULONG32("TotalBytes",       pSession->GetTotalBytes());
                                pParam->SetPropertyULONG32("TotalReqBytes",    pSession->GetTotalRequiredBytes());
                                pParam->SetPropertyULONG32("TotalNonReqBytes", pSession->GetTotalNonRequiredBytes());
                                pParam->SetPropertyULONG32("ImageWidth",       (UINT32) rImageDim.cx);
                                pParam->SetPropertyULONG32("ImageHeight",      (UINT32) rImageDim.cy);
                                // Set the outgoing parameters
                                rulNumPackets = pSession->GetNumPackets();
                                HX_RELEASE(rpParam);
                                rpParam = pParam;
                                rpParam->AddRef();
#ifdef XXXMEH_DUMP_IMAGE_PACKETS
                                FILE* fp = fopen("e:\\test\\u1bug\\packets.jpg", "wb");
                                if (fp)
                                {
                                    for (UINT32 i = 0; i < rulNumPackets; i++)
                                    {
                                        IHXBuffer* pData = NULL;
                                        IHXBuffer* pOpaq = NULL;
                                        BOOL        bReq  = FALSE;
                                        HX_RESULT rv      = pSession->GetPacket(i, pData, pOpaq, bReq);
                                        if (SUCCEEDED(rv))
                                        {
                                            fwrite(pData->GetBuffer(), 1, pData->GetSize(), fp);
                                        }
                                        HX_RELEASE(pData);
                                        HX_RELEASE(pOpaq);

                                    }
                                    fclose(fp);
                                }
#endif
                            }
                            HX_RELEASE(pParam);
                        }
                    }
                }
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
        HX_RELEASE(pSession);
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

STDMETHODIMP CRealPixJPEGFileFormatCodec::GetImagePacket(UINT32           ulSessionHandle,
                                                         UINT32           ulPacketNum,
                                                         REF(IHXBuffer*) rpPacketBuffer,
                                                         REF(IHXBuffer*) rpOpaquePacketData,
                                                         REF(BOOL)        rbRequired)
{
    HX_RESULT retVal = HXR_FAIL;

    if (m_pMapManager)
    {
        PXParseSession* pSession = NULL;
        retVal                   = m_pMapManager->GetEntry(ulSessionHandle, (void**) &pSession);
        if (SUCCEEDED(retVal))
        {
            // Get the packet from the session
            retVal = pSession->GetPacket(ulPacketNum,
                                         rpPacketBuffer,
                                         rpOpaquePacketData,
                                         rbRequired);
        }
    }

    return retVal;
}

STDMETHODIMP CRealPixJPEGFileFormatCodec::ReleaseImage(UINT32 ulSessionHandle)
{
    HX_RESULT retVal = HXR_FAIL;

    if (m_pMapManager)
    {
        PXParseSession* pSession = NULL;
        retVal                   = m_pMapManager->DeleteEntry(ulSessionHandle, (void**) &pSession);
        HX_RELEASE(pSession);
    }

    return retVal;
}

HX_RESULT STDAPICALLTYPE CRealPixJPEGFileFormatCodec::HXCreateInstance(IUnknown** ppIUnknown)
{
    HX_RESULT retVal = HXR_OK;

    if (ppIUnknown)
    {
        // Create file format codec
        CRealPixJPEGFileFormatCodec* pCodec = new CRealPixJPEGFileFormatCodec();
        if (pCodec)
        {
            // QI for IUnknown
            retVal = pCodec->QueryInterface(IID_IUnknown, (void**) ppIUnknown);
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
        if (FAILED(retVal))
        {
            HX_DELETE(pCodec);
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CRealPixJPEGFileFormatCodec::ParseBuffer(IHXBuffer*     pBuffer,
                                                   PXParseSession* pSession,
                                                   REF(HXxSize)    rImageDim)
{
    HX_RESULT retVal = HXR_OK;

    if (pBuffer && pSession)
    {
        // Get the size of the header packet, ignoring APP1-APP15
        // markers and COMment markers.
        UINT32 ulHdrSize        = 0;
        UINT32 ulNumBytesParsed = 0;
        retVal = ComputeHeaderSize(pBuffer->GetBuffer(), pBuffer->GetSize(),
                                   ulHdrSize, ulNumBytesParsed);
        if (SUCCEEDED(retVal))
        {
            // Add the first buffers
            IHXBuffer* pData             = NULL;
            IHXBuffer* pOpaque           = NULL;
            UINT32      ulIndex           = 0;
            UINT32      ulWidth           = 0;
            UINT32      ulHeight          = 0;
            UINT32      ulRestartInterval = 0;
            retVal = GetHeaderBuffers(pBuffer,   // original buffer
                                      0,         // data buffer offset
                                      ulHdrSize, // data buffer size
                                      ulIndex,   // packet index
                                      0,         // starting block
                                      0,         // num blocks
                                      pData,
                                      pOpaque,
                                      ulWidth,
                                      ulHeight,
                                      ulRestartInterval);
            if (SUCCEEDED(retVal))
            {
                // Assign the width and height
                rImageDim.cx = (INT32) ulWidth;
                rImageDim.cy = (INT32) ulHeight;
                // Increment the packet index
                ulIndex++;
                // Add the first buffers to the session
                retVal = pSession->AddPacket(pData, pOpaque, TRUE);
                if (SUCCEEDED(retVal))
                {
                    // Set the parsing pointer to the end of the header
                    BYTE* pCurByte  = pBuffer->GetBuffer() + ulNumBytesParsed;
                    BYTE* pBufLimit = pBuffer->GetBuffer() + pBuffer->GetSize();
                    // If the restart interval is non-zero, then we need to look for
                    // restart markers. This gives us some measure of loss tolerance.
                    if (ulRestartInterval > 0)
                    {
                        BOOL   bImageComplete = FALSE;
                        UINT16 usCurBlock     = 0; 
                        while (pCurByte < pBufLimit && bImageComplete == FALSE)
                        {
                            BOOL   bPacketComplete = FALSE;
                            UINT16 usNumRSTMarkers = 0;
                            BYTE  *pCurStart       = pCurByte;
                            while (bPacketComplete == FALSE && pCurByte < pBufLimit)
                            {
                                if (*pCurByte++ == 0xFF)
                                {
                                    BYTE ucMarker = *pCurByte++;
                                    if (ucMarker >= kMarkerRST0 && ucMarker <= kMarkerRST7)
                                    {
                                        usNumRSTMarkers++;
                                        if (pCurByte - pCurStart >= kDefaultPacketSize)
                                        {
                                            bPacketComplete = TRUE;
                                        }
                                    }
                                    else if (ucMarker == kMarkerEOI)
                                    {
                                        bPacketComplete = TRUE;
                                        bImageComplete = TRUE;
                                    }
                                }
                            }
                            // Did we get a valid packet?
                            if (bPacketComplete)
                            {
                                HX_RELEASE(pData);
                                HX_RELEASE(pOpaque);
                                retVal = GetBuffers(pBuffer,                          // original buffer
                                                    pCurStart - pBuffer->GetBuffer(), // data buffer offset
                                                    pCurByte - pCurStart,             // data buffer size
                                                    ulIndex,                          // packet index
                                                    usCurBlock,                       // starting block
                                                    usNumRSTMarkers,                  // num blocks
                                                    pData,
                                                    pOpaque);
                                if (SUCCEEDED(retVal))
                                {
                                    // Increment the packet index
                                    ulIndex++;
                                    // Add packet to session
                                    retVal = pSession->AddPacket(pData, pOpaque, FALSE);
                                    if (SUCCEEDED(retVal))
                                    {
                                        usCurBlock += usNumRSTMarkers;
                                    }
                                }
                            }
                            else
                            {
                                retVal = HXR_FAIL;
                            }
                            if (FAILED(retVal))
                            {
                                break;
                            }
                        }
                        // Did we finish the image OK?
                        if (!bImageComplete)
                        {
                            retVal = HXR_FAIL;
                        }
                    } // if (ulRestartInteval > 0)
                    else
                    {
                        while (pCurByte < pBufLimit)
                        {
                            BYTE *pCurStart = pCurByte;
                            pCurByte       += kDefaultPacketSize;
                            if (pCurByte > pBufLimit)
                            {
                                pCurByte = pBufLimit;
                            }
                            HX_RELEASE(pData);
                            HX_RELEASE(pOpaque);
                            retVal = GetBuffers(pBuffer,                          // original buffer
                                                pCurStart - pBuffer->GetBuffer(), // data buffer offset
                                                pCurByte - pCurStart,             // data buffer size
                                                ulIndex,                          // packet index
                                                0,                                // starting block
                                                0,                                // num blocks
                                                pData,
                                                pOpaque);
                            if (SUCCEEDED(retVal))
                            {
                                // Increment the packet index
                                ulIndex++;
                                // Add the packet to the session
                                retVal = pSession->AddPacket(pData, pOpaque, FALSE);
                            }
                            if (FAILED(retVal))
                            {
                                break;
                            }
                        }
                    } // if (ulRestartInterval > 0) else
                }
            }
            HX_RELEASE(pData);
            HX_RELEASE(pOpaque);
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;

}

HX_RESULT CRealPixJPEGFileFormatCodec::GetBuffers(IHXBuffer*      pBuffer,
                                                  UINT32           ulOffset,
                                                  UINT32           ulSize,
                                                  UINT32           ulPacketIndex,
                                                  UINT16           usCurBlock,
                                                  UINT16           usNumBlocks,
                                                  REF(IHXBuffer*) rpData,
                                                  REF(IHXBuffer*) rpOpaque)
{
    HX_RESULT retVal = HXR_OK;

    if (pBuffer)
    {
        // Create an nested buffer for the data
        CHXNestedBuffer* pNested = NULL;
        retVal                   = CHXNestedBuffer::CreateObject(&pNested);
        if (SUCCEEDED(retVal))
        {
            // Addref the nested buffer object
            pNested->AddRef();
            // Init the nested buffer object
            retVal = pNested->Init(pBuffer, ulOffset, ulSize);
            if (SUCCEEDED(retVal))
            {
                // Get an IHXBuffer interface from it
                IHXBuffer* pDataBuffer = NULL;
                retVal = pNested->QueryInterface(IID_IHXBuffer, (void**) &pDataBuffer);
                if (SUCCEEDED(retVal))
                {
                    // Create an IHXBuffer for the opaque data
                    IHXBuffer* pOpaqueBuffer = NULL;
                    retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer,
                                                                   (void**) &pOpaqueBuffer);
                    if (SUCCEEDED(retVal))
                    {
// XXXMEH - move seq num to opaque
//                        retVal = pOpaqueBuffer->SetSize(8);
                        retVal = pOpaqueBuffer->SetSize(12);
                        if (SUCCEEDED(retVal))
                        {
                            // Pack the opaque buffer
                            BYTE* pBuf = pOpaqueBuffer->GetBuffer();
// XXXMEH - move seq num to opaque
                            Pack32(pBuf, ulPacketIndex); // seq num
                            Pack32(pBuf, 0);             // flags
                            Pack16(pBuf, usCurBlock);    // starting MCU
                            Pack16(pBuf, usNumBlocks);   // number of MCUs

                            // Set the outgoing parameters
                            HX_RELEASE(rpData);
                            rpData = pDataBuffer;
                            rpData->AddRef();
                            HX_RELEASE(rpOpaque);
                            rpOpaque = pOpaqueBuffer;
                            rpOpaque->AddRef();
                        }
                    }
                    HX_RELEASE(pOpaqueBuffer);
                }
                HX_RELEASE(pDataBuffer);
            }
        }
        HX_RELEASE(pNested);
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT CRealPixJPEGFileFormatCodec::ComputeHeaderSize(BYTE* pBuf, UINT32 ulLen, REF(UINT32) rulSize,
                                                         REF(UINT32) rulNumBytesParsed)
{
    HX_RESULT retVal = HXR_OK;

    if (pBuf && ulLen)
    {
        BYTE*  pCurByte        = pBuf;
        BYTE*  pBufLimit       = pCurByte + ulLen;
        UINT32 ulRemoveBytes   = 0;
        BOOL   bHeaderComplete = FALSE;
        while (bHeaderComplete == FALSE && pCurByte < pBufLimit)
        {
            // Look for a 0xFF, which would signify a marker
            if (*pCurByte++ == 0xFF)
            {
                // Get the marker type
                BYTE ucMarker = *pCurByte++;
                // Switch based on marker type. Only the following markers
                // have parameters and therefore we need to find out the
                // length of the marker
                 if (!(ucMarker == kMarkerEOI ||
                       ucMarker == kMarkerSOI ||
                       ucMarker == kMarkerTEM ||
                      (ucMarker >= kMarkerRST0 && ucMarker <= kMarkerRST7)))
                {
                    UINT32 ulSegLen = (pCurByte[0] << 8) | pCurByte[1];
                    if (ucMarker == kMarkerAPP0)
                    {
                        // We look at this marker to verify this is a JFIF image file
                        if (pCurByte[0] != 0x00 || pCurByte[1] != 0x10 ||
                            pCurByte[2] != 'J'  || pCurByte[3] != 'F'  ||
                            pCurByte[4] != 'I'  || pCurByte[5] != 'F'  ||
                            pCurByte[6] != '\0')
                        {
                            retVal = HXR_FAIL;
                            break;
                        }
                    }
                    else if (ucMarker >= kMarkerAPP1 && ucMarker <= kMarkerAPP15)
                    {
                        // We will NOT send these markers. ulSegLen is the length
                        // of the marker (including the 2-byte-length, but excluding
                        // the 0xFF 0x<marker>
                        ulRemoveBytes += ulSegLen + 2;
                    }
                    else if (ucMarker == kMarkerCOM)
                    {
                        // We will NOT send comments
                        ulRemoveBytes += ulSegLen + 2;
                    }
                    else if (ucMarker == kMarkerSOS)
                    {
                        // This marker is the last marker in the header
                        bHeaderComplete = TRUE;
                    }
                    // Advance the pointer
                    pCurByte += ulSegLen;
                }
            }
        }

        if (bHeaderComplete)
        {
            rulNumBytesParsed = pCurByte - pBuf;
            rulSize           = rulNumBytesParsed - ulRemoveBytes;
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

void CRealPixJPEGFileFormatCodec::ReleaseAllSessions()
{
    if (m_pMapManager)
    {
        UINT32          ulHandle = 0;
        PXParseSession* pSession = NULL;
        HX_RESULT       retVal   = m_pMapManager->GetFirstEntry(ulHandle, (void**) &pSession);
        while (SUCCEEDED(retVal))
        {
            HX_RELEASE(pSession);
            retVal = m_pMapManager->GetNextEntry(ulHandle, (void**) &pSession);
        }
        m_pMapManager->DeleteAllEntries();
    }
}

HX_RESULT CRealPixJPEGFileFormatCodec::GetHeaderBuffers(IHXBuffer*      pBuffer,
                                                        UINT32           ulOffset,
                                                        UINT32           ulSize,
                                                        UINT32           ulPacketIndex,
                                                        UINT16           usCurBlock,
                                                        UINT16           usNumBlocks,
                                                        REF(IHXBuffer*) rpData,
                                                        REF(IHXBuffer*) rpOpaque,
                                                        REF(UINT32)      rulWidth,
                                                        REF(UINT32)      rulHeight,
                                                        REF(UINT32)      rulRestartInterval)
{
    HX_RESULT retVal = HXR_OK;

    if (pBuffer && ulSize)
    {
        // Create an IHXBuffer for the data (from the file) packet buffer
        IHXBuffer* pDataBuffer = NULL;
        retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer,
                                                       (void**) &pDataBuffer);
        if (SUCCEEDED(retVal))
        {
            // Set its size
            retVal = pDataBuffer->SetSize(ulSize);
            if (SUCCEEDED(retVal))
            {
                retVal = CopyHeaderStripMarkers(pBuffer->GetBuffer(), pBuffer->GetSize(),
                                                pDataBuffer->GetBuffer(), pDataBuffer->GetSize(),
                                                rulWidth, rulHeight, rulRestartInterval);
                if (SUCCEEDED(retVal))
                {
                    // Create an IHXBuffer for the opaque data
                    IHXBuffer* pOpaqueBuffer = NULL;
                    retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer,
                                                                   (void**) &pOpaqueBuffer);
                    if (SUCCEEDED(retVal))
                    {
// XXXMEH - move seq num to opaque
//                        retVal = pOpaqueBuffer->SetSize(8);
                        retVal = pOpaqueBuffer->SetSize(12);
                        if (SUCCEEDED(retVal))
                        {
                            // Pack the opaque buffer
                            BYTE* pBuf = pOpaqueBuffer->GetBuffer();
// XXXMEH - move seq num to opaque
                            Pack32(pBuf, ulPacketIndex); // seq num
                            Pack32(pBuf, 0);             // flags
                            Pack16(pBuf, usCurBlock);    // starting MCU
                            Pack16(pBuf, usNumBlocks);   // number of MCUs

                            // Set the outgoing parameters
                            HX_RELEASE(rpData);
                            rpData = pDataBuffer;
                            rpData->AddRef();
                            HX_RELEASE(rpOpaque);
                            rpOpaque = pOpaqueBuffer;
                            rpOpaque->AddRef();
                        }
                    }
                    HX_RELEASE(pOpaqueBuffer);
                }
            }
        }
        HX_RELEASE(pDataBuffer);
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

HX_RESULT CRealPixJPEGFileFormatCodec::CopyHeaderStripMarkers(BYTE*       pOrigBuf,
                                                              UINT32      ulOrigBufLen,
                                                              BYTE*       pHdrBuf,
                                                              UINT32      ulHdrBufLen,
                                                              REF(UINT32) rulWidth,
                                                              REF(UINT32) rulHeight,
                                                              REF(UINT32) rulRestartInterval)
{
    HX_RESULT retVal = HXR_OK;

    if (pOrigBuf && ulOrigBufLen && pHdrBuf && ulHdrBufLen)
    {
        BYTE* pSrc            = pOrigBuf;
        BYTE* pSrcLimit       = pSrc + ulOrigBufLen;
        BYTE* pDst            = pHdrBuf;
        BYTE* pDstLimit       = pDst + ulHdrBufLen;
        BOOL  bHeaderComplete = FALSE;
        while (!bHeaderComplete && pSrc < pSrcLimit && pDst < pDstLimit)
        {
            // Look for a 0xFF, which would signify a marker
            if (pSrc[0] == 0xFF)
            {
                // Get the marker type
                BYTE ucMarker = pSrc[1];
                // Switch based on marker type. Only the following markers
                // have parameters and therefore we need to find out the
                // length of the marker
                if (!(ucMarker == kMarkerEOI ||
                      ucMarker == kMarkerSOI ||
                      ucMarker == kMarkerTEM ||
                      (ucMarker >= kMarkerRST0 && ucMarker <= kMarkerRST7)))
                {
                    // Compute the length
                    UINT32 ulSegLen = (pSrc[2] << 8) | pSrc[3];
                    // Get info out of some markers
                    if (ucMarker >= kMarkerSOF0 && ucMarker <= kMarkerSOF15 &&
                        ucMarker != kMarkerDHT  && ucMarker != kMarkerDAC   &&
                        ucMarker != 0xC8)
                    {
                        // We look at this marker to extract the width and height
                        rulHeight = (pSrc[5] << 8) | pSrc[6];
                        rulWidth  = (pSrc[7] << 8) | pSrc[8];
                    }
                    else if (ucMarker == kMarkerDRI)
                    {
                        // We look at this marker to extract the restart interval
                        rulRestartInterval = (pSrc[4] << 8) | pSrc[5];
                    }
                    else if (ucMarker == kMarkerSOS)
                    {
                        bHeaderComplete = TRUE;
                    }
                    // If we are NOT an APP1-APP15 marker and we are NOT a COMment
                    // marker, then we need to copy
                    if (!(ucMarker == kMarkerCOM ||
                          (ucMarker >= kMarkerAPP1 && ucMarker <= kMarkerAPP15)))
                    {
			// /Make sure pDst+ulSegLen+2 <= pDstLimit before copying:
			UINT32 ulSafeLenToCopy = ulSegLen + 2;
			if (ulHdrBufLen < ulSafeLenToCopy)
			{
			    ulSafeLenToCopy = ulHdrBufLen;
			}
                        // Copy the data
                        memcpy(pDst, pSrc, ulSafeLenToCopy); /* Flawfinder: ignore */
                        // Advance the dst pointer
                        pDst += ulSegLen + 2;
                    }
                    // Advance the src pointer
                    pSrc += ulSegLen + 2;
                }
                else
                {
                    *pDst++ = *pSrc++;
                    *pDst++ = *pSrc++;
                }
            }
            else
            {
                *pDst++ = *pSrc++;
            }
        }

        if (!bHeaderComplete)
        {
            retVal = HXR_FAIL;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

