/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: pxpngff.cpp,v 1.3.24.1 2004/07/09 01:52:41 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 "hxcom.h"
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxcomm.h"
#include "ihxpckts.h"
#include "hxfiles.h"
#include "hxformt.h"
#include "hxplugn.h"
#include "hxpends.h"
#include "hxasm.h"
#include "hxerror.h"
#include "hxver.h"
#include "hxxres.h"
#include "hxxrsmg.h"
#include "ihxfgbuf.h"

// pncont
#include "chxpckts.h"
#include "hxstring.h"
#include "chxfgbuf.h"

// pnmisc
#include "hxurl.h"
#include "unkimp.h"
#include "baseobj.h"
#include "hxparse.h"
#ifdef _AIX
#include "dllpath.h"
ENABLE_MULTILOAD_DLLACCESS_PATHS(Pxpngff);
#endif

// pxcomlib
#include "gstring.h"
#include "pxcolor.h"
#include "parseurl.h"
#include "pxutil.h"
#include "nestbuff.h"

// libpng
#include "png.h"

// pxpnglib
#include "pxpngdec.h"

// coreres
#include "pixres.h"

// pxpngff
#include "pxpngff.h"
#include "pngfdll.ver"

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

const char* const  PXPNGFileFormat::m_pszDescription       = "Helix PNG File Format Plugin";
const char* const  PXPNGFileFormat::m_pszCopyright         = HXVER_COPYRIGHT;
const char* const  PXPNGFileFormat::m_pszMoreInfoURL       = HXVER_MOREINFO;
const char* const  PXPNGFileFormat::m_ppszFileMimeTypes[]  = {"image/png", NULL};
const char* const  PXPNGFileFormat::m_ppszFileExtensions[] = {"png", NULL};
const char* const  PXPNGFileFormat::m_ppszFileOpenNames[]  = {"PNG Images (*.png)", NULL};
const char* const  PXPNGFileFormat::m_pszStreamMimeType    = "application/vnd.rn-pngstream";
const UINT32 PXPNGFileFormat::m_ulContentVersion     = HX_ENCODE_PROD_VERSION(0, 0, 0, 0);
const UINT32 PXPNGFileFormat::m_ulStreamVersion      = HX_ENCODE_PROD_VERSION(0, 0, 0, 0);

#ifdef _WINDOWS
extern HINSTANCE g_hInstance;
#endif

PXPNGFileFormat::PXPNGFileFormat()
{
    m_lRefCount = 0;
    Reset();
};

PXPNGFileFormat::~PXPNGFileFormat()
{
    Deallocate();
};

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

    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_IHXFileFormatObject))
    {
        AddRef();
        *ppvObj = (IHXFileFormatObject*) this;
    }
    else if (IsEqualIID(riid, IID_IHXFileResponse))
    {
        AddRef();
        *ppvObj = (IHXFileResponse*) this;
    }
    else
    {
        *ppvObj = NULL;
        retVal  = HXR_NOINTERFACE;
    }

    return retVal;
}

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

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

    delete this;

    return 0;
}

STDMETHODIMP PXPNGFileFormat::GetPluginInfo(REF(BOOL)        rbLoadMultiple,
                                            REF(const char*) rpszDescription,
                                            REF(const char*) rpszCopyright,
                                            REF(const char*) rpszMoreInfoURL,
                                            REF(UINT32)      rulVersionNumber)
{
    rbLoadMultiple   = TRUE;
    rpszDescription  = m_pszDescription;
    rpszCopyright    = m_pszCopyright;
    rpszMoreInfoURL  = m_pszMoreInfoURL;
    rulVersionNumber = TARVER_ULONG32_VERSION;

    return HXR_OK;
}

STDMETHODIMP PXPNGFileFormat::InitPlugin(IUnknown* pContext)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pContext)
    {
        // Clear out everything
        Deallocate();
        // Save a copy of the calling context
        m_pContext = pContext;
        m_pContext->AddRef();
        // Get an interface to the common class factory
        retVal = m_pContext->QueryInterface(IID_IHXCommonClassFactory,
                                            (void**) &m_pCommonClassFactory);
        if (SUCCEEDED(retVal))
        {
            // Set the state
            m_ulState = kStatePluginInitialized;
        }
    }

    return retVal;
}


STDMETHODIMP PXPNGFileFormat::GetFileFormatInfo(REF(const char**) rppszFileMimeTypes,
                                                REF(const char**) rppszFileExtensions,  
                                                REF(const char**) rppszFileOpenNames)
{
    rppszFileMimeTypes  = (const char **) m_ppszFileMimeTypes;
    rppszFileExtensions = (const char **) m_ppszFileExtensions;
    rppszFileOpenNames  = (const char **) m_ppszFileOpenNames;

    return HXR_OK;
}

STDMETHODIMP PXPNGFileFormat::InitFileFormat(IHXRequest*        pRequest, 
                                             IHXFormatResponse* pFormatResponse,
                                             IHXFileObject*     pFileObject)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pRequest && pFormatResponse && pFileObject)
    {
        if (m_ulState == kStatePluginInitialized)
        {
            // Save members
            HX_RELEASE(m_pRequest);
            m_pRequest = pRequest;
            m_pRequest->AddRef();
            HX_RELEASE(m_pFormatResponse);
            m_pFormatResponse = pFormatResponse;
            m_pFormatResponse->AddRef();
            HX_RELEASE(m_pFileObject);
            m_pFileObject = pFileObject;
            m_pFileObject->AddRef();
            // Get the URL-encoded options
            GetURLOrRequestOptions(pRequest,
                                   m_ulDuration,
                                   m_ulBitrate,
                                   m_pURLStr,
                                   m_bReliable,
                                   m_ulBackgroundColor);
            // Set the state
            m_ulState = kStateFileInitPending;
            // Init the file object
            retVal = m_pFileObject->Init(HX_FILE_READ | HX_FILE_BINARY, this);
        }
    }

    if (FAILED(retVal) && pFormatResponse)
    {
        pFormatResponse->InitDone(HXR_FAIL);
    }

    return retVal;
}

STDMETHODIMP PXPNGFileFormat::GetFileHeader()
{
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateFileFormatInitialized)
    {
        // Get an IHXValues object
        IHXValues* pFileHeader = NULL;
        retVal                  = m_pCommonClassFactory->CreateInstance(CLSID_IHXValues,
                                                                        (void**) &pFileHeader);
        if (SUCCEEDED(retVal))
        {
            // Set the stream count
            pFileHeader->SetPropertyULONG32("StreamCount",    1);
            pFileHeader->SetPropertyULONG32("IsRealDataType", 1);
            // Set the width and height
            pFileHeader->SetPropertyULONG32("Width",  m_ulImageWidth);
            pFileHeader->SetPropertyULONG32("Height", m_ulImageHeight);
            // Set the new state
            m_ulState = kStateFileHeaderSent;
            // Call the response interface
            m_pFormatResponse->FileHeaderReady(HXR_OK, pFileHeader);
        }
        HX_RELEASE(pFileHeader);

        if (FAILED(retVal))
        {
            m_pFormatResponse->FileHeaderReady(retVal, NULL);
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

STDMETHODIMP PXPNGFileFormat::GetStreamHeader(UINT16 usStreamNum)
{
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateFileHeaderSent)
    {
        // Create an IHXValues object
        IHXValues* pStreamHeader = NULL;
        retVal                    = m_pCommonClassFactory->CreateInstance(CLSID_IHXValues,
                                                                          (void**) &pStreamHeader);
        if (SUCCEEDED(retVal))
        {
            // Create mime type IHXBuffer
            IHXBuffer* pMimeStr = NULL;
            retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer,
                                                           (void **) &pMimeStr);
            if (SUCCEEDED(retVal))
            {
                // Fill in the mime type
                retVal = pMimeStr->Set((const BYTE*) m_pszStreamMimeType,
                                       strlen(m_pszStreamMimeType) + 1);
                if (SUCCEEDED(retVal))
                {
                    // Create an opaque data buffer object
                    IHXBuffer* pOpaque = NULL;
                    retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer,
                                                                   (void**) &pOpaque);

		    IHXBuffer* pIntrinsicDurationType = NULL;
		    if (SUCCEEDED(retVal))
		    {
			// /Create intrinsicDurationType IHXBuffer:
			retVal= m_pCommonClassFactory->CreateInstance(
				CLSID_IHXBuffer,
				(void **) &pIntrinsicDurationType);
			if (SUCCEEDED(retVal))
			{
			    // /This is a "discrete" media stream; it has no
			    // intrinsic (native) duration; we want to
			    // continue declaring 5000 for the duration,
			    // however, since we may be serving this stream
			    // to an old, SMIL 1.0 presentation or as a
			    // stand-alone play in which the author expects
			    // the old 5000-msec duration:
			    retVal = pIntrinsicDurationType->Set(
				  (const BYTE*)"intrinsicDurationDiscrete",
				  strlen("intrinsicDurationDiscrete") + 1);
			}
		    }
                    if (SUCCEEDED(retVal))
                    {
                        // Compute the size of the opaque buffer
                        UINT32 ulOpaqueSize = 4 + // image width
                                              4 + // image height
                                              4 + // number of packets
                                              4 + // background color
                                              2 + // url buffer size
                                              (m_pURLStr ? m_pURLStr->GetSize() : 0); // url buffer
                        // Set the size of the opaque buffer
                        retVal = pOpaque->SetSize(ulOpaqueSize);
                        if (SUCCEEDED(retVal))
                        {
                            // Pack the opaque data
                            BYTE* pBuf = pOpaque->GetBuffer();
                            Pack32(pBuf, m_ulImageWidth);
                            Pack32(pBuf, m_ulImageHeight);
                            Pack32(pBuf, m_ulNumPacketBuffers);
                            Pack32(pBuf, m_ulBackgroundColor);
                            Pack16(pBuf, (UINT16) (m_pURLStr ? m_pURLStr->GetSize() : 0));
                            if (m_pURLStr)
                            {
                                memcpy(pBuf, m_pURLStr->GetBuffer(), m_pURLStr->GetSize()); /* Flawfinder: ignore */
                            }
                            // Create ASM rulebook string
                            char szASM[256]; /* Flawfinder: ignore */
                            if (m_bReliable)
                            {
                                sprintf(szASM, "AverageBandwidth=%lu,Priority=10;", m_ulBitrate); /* Flawfinder: ignore */
                            }
                            else
                            {
                                // XXXMEH-TODO: compute % of high priority packets
                                sprintf(szASM, /* Flawfinder: ignore */
                                        "AverageBandwidth=%lu,Priority=5;AverageBandwidth=0,Priority=10;",
                                        m_ulBitrate);
                            }
                            // Create ASM rulebook buffer
                            IHXBuffer* pASMStr = NULL;
                            retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer,
                                                                           (void**) &pASMStr);
                            if (SUCCEEDED(retVal))
                            {
                                retVal = pASMStr->Set((const BYTE*) szASM,
                                                      (UINT32) strlen(szASM) + 1);
                                if (SUCCEEDED(retVal))
                                {
                                    // Set the properties
                                    pStreamHeader->SetPropertyBuffer ("OpaqueData",       pOpaque);
                                    pStreamHeader->SetPropertyULONG32("StreamNumber",     0);
                                    pStreamHeader->SetPropertyULONG32("MaxBitRate",       m_ulBitrate);
                                    pStreamHeader->SetPropertyULONG32("AvgBitRate",       m_ulBitrate);
                                    pStreamHeader->SetPropertyULONG32("MaxPacketSize",    m_ulMaxPacketSize);
                                    pStreamHeader->SetPropertyULONG32("AvgPacketSize",    m_ulAvgPacketSize);
                                    pStreamHeader->SetPropertyULONG32("StartTime",        0);
                                    pStreamHeader->SetPropertyULONG32("PreDataAtStart",   1);
                                    pStreamHeader->SetPropertyULONG32("PreRollAfterSeek", 1);
                                    pStreamHeader->SetPropertyULONG32("PreData",          m_ulTotalBytesToSend);
                                    pStreamHeader->SetPropertyULONG32("PreRoll",          kDefaultPreroll);
                                    pStreamHeader->SetPropertyULONG32("Duration",         m_ulDuration);
                                    pStreamHeader->SetPropertyCString("MimeType",         pMimeStr);
                                    pStreamHeader->SetPropertyULONG32("ContentVersion",   m_ulContentVersion);
                                    pStreamHeader->SetPropertyCString("ASMRuleBook",      pASMStr);
                                    pStreamHeader->SetPropertyULONG32("StreamVersion",    m_ulStreamVersion);
                                    // We need to declare that this is a
                                    // discrete media type so that the SMIL 2
                                    // renderer can treat it as such.  We
                                    // don't want to just set the duration to
                                    // 0 for this as that would break old SMIL
                                    // 1.0 and non-SMIL content when someone
                                    // switched image servers to include a new
                                    // build of this png file format plug-in:
                                    pStreamHeader->SetPropertyCString("intrinsicDurationType",
                                                                      pIntrinsicDurationType);
                                    // Set the new state
                                    m_ulState = kStateStreamHeaderSent;
                                    // Reset the next packet counter
                                    m_ulNextPacketIndex = 0;
                                    // Pass the stream header back to the server
                                    m_pFormatResponse->StreamHeaderReady(HXR_OK, pStreamHeader);
                                }
                            }
                            HX_RELEASE(pASMStr);
                        }
                    }
                    HX_RELEASE(pOpaque);
		    HX_RELEASE(pIntrinsicDurationType);
                }
            }
            HX_RELEASE(pMimeStr);
        }
        HX_RELEASE(pStreamHeader);

        if (FAILED(retVal))
        {
            m_pFormatResponse->StreamHeaderReady(retVal, NULL);
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

STDMETHODIMP PXPNGFileFormat::GetPacket(UINT16 usStreamNum)
{
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateStreamHeaderSent)
    {
        if (usStreamNum == 0)
        {
            if (m_ulNextPacketIndex < m_ulNumPacketBuffers)
            {
                // Get the opaque buffer from the array
                IHXBuffer* pBuffer = m_ppPacketBuffer[m_ulNextPacketIndex];
                if (pBuffer)
                {
                    // AddRef the buffer
                    pBuffer->AddRef();
                    // Create an IHXPacket
                    IHXPacket* pPacket = NULL;
                    retVal              = m_pCommonClassFactory->CreateInstance(CLSID_IHXPacket,
                                                                                (void**) &pPacket);
                    if (SUCCEEDED(retVal))
                    {
                        // Set the values in the packet
                        // We use ASM rule 1 if this is the first packet; otherwise
                        // we rule ASM rule 0
                        UINT16 usASMRule = (UINT16) (m_ulNextPacketIndex ? 0 : 1);
                        retVal = pPacket->Set(pBuffer,           // opaque data
                                              0,                 // time stamp
                                              0,                 // stream 0
                                              HX_ASM_SWITCH_ON, // ASM flag
                                              usASMRule);        // ASM rule
                        if (SUCCEEDED(retVal))
                        {
                            // Increment the packet counter
                            m_ulNextPacketIndex++;
                            // Return the packet
                            m_pFormatResponse->PacketReady(HXR_OK, pPacket);
                        }
                    }
                    HX_RELEASE(pPacket);
                }
                HX_RELEASE(pBuffer);
            }
            else
            {
                // We're finished
                m_pFormatResponse->StreamDone(0);
            }
        }
        else
        {
            retVal = HXR_INVALID_PARAMETER;
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

STDMETHODIMP PXPNGFileFormat::Seek(UINT32 ulOffset)
{
    HX_RESULT retVal = HXR_OK;

    if (m_pFormatResponse)
    {
        m_pFormatResponse->SeekDone(HXR_OK);
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

STDMETHODIMP PXPNGFileFormat::Close()
{
    HX_RESULT retVal = HXR_OK;

    Deallocate();

    return retVal;
}

STDMETHODIMP PXPNGFileFormat::InitDone(HX_RESULT status)
{
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateFileInitPending)
    {
        if (SUCCEEDED(status))
        {
            // Create an IHXFragmentedBuffer object
            CHXFragmentedBuffer* pFragBuffer = NULL;
            retVal = CHXFragmentedBuffer::CreateObject(&pFragBuffer);
            if (SUCCEEDED(retVal))
            {
                // AddRef the object
                pFragBuffer->AddRef();
                // QI it for IHXFragmentedBuffer
                retVal = pFragBuffer->QueryInterface(IID_IHXFragmentedBuffer,
                                                     (void**) &m_pFragBuffer);
            }
            HX_RELEASE(pFragBuffer);

            if (SUCCEEDED(retVal))
            {
                // Reset the bytes read counter
                m_ulNumBytesRead = 0;
                // Set the state
                m_ulState = kStateFileReadPending;
                // Do the first read from the file
                m_pFileObject->Read(kReadSize);
            }
        }
        else
        {
            retVal = status;
        }

        if (FAILED(retVal))
        {
            m_pFormatResponse->InitDone(retVal);
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

STDMETHODIMP PXPNGFileFormat::ReadDone(HX_RESULT status, IHXBuffer* pBuffer)
{
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateFileReadPending)
    {
        if (SUCCEEDED(status))
        {
            retVal = m_pFragBuffer->Append(pBuffer, 0, pBuffer->GetSize());
            if (SUCCEEDED(retVal))
            {
                m_ulNumBytesRead += pBuffer->GetSize();
            }
        }

        if (SUCCEEDED(retVal))
        {
            if ((SUCCEEDED(status) && pBuffer->GetSize() < kReadSize) || FAILED(status))
            {
                if (m_ulNumBytesRead > 0)
                {
                    // Get an IHXBuffer interface from the IHXFragmentedBuffer
                    HX_RELEASE(m_pFileBuffer);
                    retVal = m_pFragBuffer->QueryInterface(IID_IHXBuffer, (void**) &m_pFileBuffer);
                    if (SUCCEEDED(retVal))
                    {
                        // Now we can release the IHXFragmentedBuffer interface
                        HX_RELEASE(m_pFragBuffer);
                        // Set state
                        m_ulState = kStateFileClosePending;
                        // Close the file
                        m_pFileObject->Close();
                    }
                }
                else
                {
                    retVal = HXR_FAIL;
                }
            }
            else
            {
                // Set state
                m_ulState = kStateFileReadPending;
                // Read some more
                m_pFileObject->Read(kReadSize);
            }
        }

        if (FAILED(retVal))
        {
            m_pFormatResponse->InitDone(retVal);
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}


STDMETHODIMP PXPNGFileFormat::SeekDone(HX_RESULT status)
{
    return HXR_UNEXPECTED;
}

STDMETHODIMP PXPNGFileFormat::CloseDone(HX_RESULT status)
{
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateFileClosePending)
    {
        // Get the width and height out of the IHDR chunk
        if (PXPNGDecode::GetIHDRInfo(m_pFileBuffer, m_ulImageWidth, m_ulImageHeight))
        {
            // Now we have the entire file, so first we determine the number
            // of packets. We do this by finding the beginning of the first
            // IDAT chunk.
            UINT32    ulOffset  = 0;
            UINT32    ulBytes   = 0;
            BOOL      bComplete = FALSE;
            BOOL      bPresent  = PXPNGDecode::IsChunkPresent(m_pFileBuffer,
                                                              0x49444154, // IDAT chunk
                                                              ulOffset,
                                                              ulBytes,
                                                              bComplete);
            if (bPresent && bComplete)
            {
                UINT32 ulFirstBufferSize = 0;
                if (ulOffset + kMinIDATBytes > kDefaultPacketSize)
                {
                    // Since the size of the all the chunks before the IDAT is
                    // greater than the ideal packet size, then we'll just do
                    // the minimum here.
                    ulFirstBufferSize   = ulOffset + kMinIDATBytes;
                }
                else
                {
                    // Since the size of all the chunks before the IDAT is
                    // LESS than the ideal packet size, we'll allow some of
                    // the IDAT chunk into the first packet.
                    ulFirstBufferSize = kDefaultPacketSize;
                    if (ulFirstBufferSize > m_pFileBuffer->GetSize())
                    {
                        ulFirstBufferSize = m_pFileBuffer->GetSize();
                    }
                }
                // Add the first packet buffer
                m_ulNumPacketBuffers  = 1;
                // Add all the rest of the packets past the first
                m_ulNumPacketBuffers += (m_pFileBuffer->GetSize() - ulFirstBufferSize +
                                         kDefaultPacketSize - 1) /
                                         kDefaultPacketSize;
                // So now that we know how many packets we'll have, we
                // can allocate the array of IHXBuffer pointers to hold
                // them.
                HX_VECTOR_DELETE(m_ppPacketBuffer);
                m_ppPacketBuffer = new IHXBuffer* [m_ulNumPacketBuffers];
                if (m_ppPacketBuffer)
                {
                    // Init the statistics
                    m_ulMaxPacketSize = 0;
                    m_ulAvgPacketSize = 0;
                    // Now run through and create the buffers
                    UINT32 ulCurOffset = 0;
                    UINT32 ulCurSize   = ulFirstBufferSize;
                    for (UINT32 i = 0; i < m_ulNumPacketBuffers && SUCCEEDED(retVal); i++)
                    {
                        // Create a nested buffer
                        CHXNestedBuffer* pNest = NULL;
                        retVal                 = CHXNestedBuffer::CreateObject(&pNest);
                        if (SUCCEEDED(retVal))
                        {
                            // AddRef the object
                            pNest->AddRef();
                            // Init the nested buffer
                            retVal = pNest->Init(m_pFileBuffer, ulCurOffset, ulCurSize);
                            if (SUCCEEDED(retVal))
                            {
                                // QI for IHXBuffer
                                IHXBuffer* pData = NULL;
                                retVal            = pNest->QueryInterface(IID_IHXBuffer,
                                                                          (void**) &pData);
                                if (SUCCEEDED(retVal))
                                {
                                    // Update the statistics
                                    if (ulCurSize > m_ulMaxPacketSize)
                                    {
                                        m_ulMaxPacketSize = ulCurSize;
                                    }
                                    m_ulAvgPacketSize += ulCurSize;
                                    // Update the current offset and size
                                    ulCurOffset += ulCurSize;
                                    ulCurSize    = kDefaultPacketSize;
                                    if (ulCurOffset + ulCurSize > m_pFileBuffer->GetSize())
                                    {
                                        ulCurSize = m_pFileBuffer->GetSize() - ulCurOffset;
                                    }
                                    // Put it in the array
                                    m_ppPacketBuffer[i] = pData;
                                    m_ppPacketBuffer[i]->AddRef();
                                }
                                HX_RELEASE(pData);
                            }
                        }
                        HX_RELEASE(pNest);
                    }
                    // Compute the avg
                    m_ulAvgPacketSize    = (m_ulAvgPacketSize + (m_ulNumPacketBuffers >> 1)) /
                                           m_ulNumPacketBuffers;
                    m_ulTotalBytesToSend = m_pFileBuffer->GetSize();
                    // Now we can release the file buffer, since all the
                    // nested buffers have refs on it
                    HX_RELEASE(m_pFileBuffer);
                    // Set the state
                    m_ulState = (SUCCEEDED(retVal) ? kStateFileFormatInitialized : kStateError);
                    // Callback to the response interface
                    m_pFormatResponse->InitDone(retVal);
                }
                else
                {
                    retVal = HXR_OUTOFMEMORY;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

STDMETHODIMP PXPNGFileFormat::WriteDone(HX_RESULT status)
{
    // We don't ever write, so we don't expect to get this...
    return HXR_UNEXPECTED;
}

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

    if (ppIUnknown)
    {
        // Set default
        *ppIUnknown = NULL;
        // Create the object
        PXPNGFileFormat *pObj = new PXPNGFileFormat();
        if (pObj)
        {
            // QI for IUnknown
            retVal = pObj->QueryInterface(IID_IUnknown, (void**) ppIUnknown);
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
        if (FAILED(retVal))
        {
            HX_DELETE(pObj);
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return HXR_OK;
}

void PXPNGFileFormat::Deallocate()
{
    for (UINT32 i = 0; i < m_ulNumPacketBuffers; i++)
    {
        HX_RELEASE(m_ppPacketBuffer[i]);
    }
    HX_RELEASE(m_pContext);
    HX_RELEASE(m_pFileObject);
    HX_RELEASE(m_pFormatResponse);
    HX_RELEASE(m_pCommonClassFactory);
    HX_RELEASE(m_pRequest);
    HX_RELEASE(m_pFragBuffer);
    HX_RELEASE(m_pFileBuffer);
    HX_VECTOR_DELETE(m_ppPacketBuffer);
    HX_RELEASE(m_pURLStr);
    Reset();
}

void PXPNGFileFormat::Reset()
{
    m_pContext            = NULL;
    m_pFileObject         = NULL;
    m_pFormatResponse     = NULL;
    m_pCommonClassFactory = NULL;
    m_pRequest            = NULL;
    m_pFragBuffer         = NULL;
    m_ulNumBytesRead      = 0;
    m_pFileBuffer         = NULL;
    m_ulNumPacketBuffers  = 0;
    m_ppPacketBuffer      = NULL;
    m_ulState             = kStateConstructed;
    m_ulDuration          = kDefaultDuration;
    m_ulBitrate           = kDefaultBitrate;
    m_ulImageWidth        = 0;
    m_ulImageHeight       = 0;
    m_pURLStr             = NULL;
    m_bReliable           = FALSE;
    m_ulTotalBytesToSend  = 0;
    m_ulMaxPacketSize     = 0;
    m_ulAvgPacketSize     = 0;
    m_ulBackgroundColor   = 0;
    m_ulNextPacketIndex   = 0;
}

void PXPNGFileFormat::GetURLOrRequestOptions(IHXRequest*     pRequest,
                                             REF(UINT32)      rulDuration,
                                             REF(UINT32)      rulBitrate,
                                             REF(IHXBuffer*) rpURLStr,
                                             REF(BOOL)        rbReliable,
                                             REF(UINT32)      rulBackgroundColor)
{
    if (pRequest && m_pContext && m_pCommonClassFactory)
    {
        // Create an IHXValues object to hold the param name/value pairs
        IHXValues *pValues = NULL;
        m_pCommonClassFactory->CreateInstance(CLSID_IHXValues,
                                              (void**) &pValues);
        if (pValues)
        {
            // Now add the following parameters to the IHXValues. These
            // parameters could have come as URL-encoded parameters or as
            // parameters in the request headers. These parameters will be
            // added as CString properties to the IHXValues.
            AddURLOrRequestParam(pRequest, "duration",                m_pContext, pValues);
            AddURLOrRequestParam(pRequest, "bitrate",                 m_pContext, pValues);
            AddURLOrRequestParam(pRequest, "url",                     m_pContext, pValues);
            AddURLOrRequestParam(pRequest, "reliable",                m_pContext, pValues);
            AddURLOrRequestParam(pRequest, "bgcolor",                 m_pContext, pValues);
            // Set the bitrate
            ExtractValueUINT32(pValues, "bitrate",  kDefaultBitrate,  rulBitrate);
            // Set the duration
            ExtractValueTime(pValues,   "duration", kDefaultDuration, rulDuration);
            // Set the URL
            HX_RELEASE(rpURLStr);
            pValues->GetPropertyCString("url", rpURLStr);
            // Set the background color
            IHXBuffer* pTmp = NULL;
            pValues->GetPropertyCString("bgcolor", pTmp);
            if (pTmp)
            {
                HXParseColorUINT32((const char*) pTmp->GetBuffer(),
                                   rulBackgroundColor);
            }
            HX_RELEASE(pTmp);
            // Set the reliable flag
            ExtractValueBOOL(pValues, "reliable", FALSE, rbReliable);
        }
        // Release the IHXValues object
        HX_RELEASE(pValues);
    }
}
