Logo Search packages:      
Sourcecode: helix-player version File versions

rtffplin.cpp

/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: rtffplin.cpp,v 1.1.2.1 2004/07/09 01:50:34 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 ***** */

/////////////////////////////////////////////////////////////////////////////
// 
//  rtffplin.cpp
// 
//  REALTEXT FILE FORMAT
//
//
//

#define HEADER_OFFSET             0
#define MAX_HEADER_SIZE           256 
#define MAX_PKT_HDR_SZ            1024 
#define MAX_PACKET_SIZE           500 
#define AVG_PACKET_SIZE           493 
#define MIN_PACKET_SIZE           256 
#define TIME_PER_PACKET           1000
#define DEFAULT_PREROLL_MSEC      0 
#define DEFAULT_DURATION_MSEC     60000


#define INITGUID

#include "rtffplin.ver"

#include "hxtypes.h"
#include "hxcom.h"
#include "hxcomm.h"
#include "ihxpckts.h"
#include "hxfiles.h"
#include "hxformt.h"
#include "hxplugn.h"
#include "hxpends.h"
#include "hxmon.h"
#include "hxerror.h"
#include "hxxres.h"
#include "hxxrsmg.h"
#include "hxcore.h"
#include "hxvsrc.h"
#include "rtres.h"
#include "perplex.h"

#include "hxstack.h"
#include "hxslist.h"

#include "rt_types.h"
#include "rltxthdr.h"

#include "fontdefs.h"
#include "txtattrb.h"
#include "txtcntnr.h"
#include "textline.h"

#include "verutil.h" //for IsBeta1Player().

#ifdef _WINDOWS
#ifdef _WIN16
#include <windows.h>
#endif /* _WIN16 */
#endif /* _WINDOWS */

#include "txtwindw.h" //Added for class TextWindow.
#include "textprsr.h" //Added for base class TextParser.

#include "hxstrutl.h"  //for strncasecmp()

#include <string.h>
#include <stdio.h>  // For FILE (if defined _DEBUG && RT_OUTPUT_LOGFILE).

#include "hxengin.h"
#include "rtffplin.h"
#include "chxfgbuf.h"
#include "shadvsrc.h" /* CShadowViewSource */

#include "hxver.h"
#include "defslice.h"

#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(Rtffplin);
#endif

INT32 g_nRefCount_rtff = 0;

//This is a rough estimate of the size of the stream divided by the size of
// the original rt data in the file, i.e., if the following is 1.XY, then
// the opaque headers added to each packet increase the packet size by
// about XY percent:
#define HEADER_OVERHEAD_FACTOR      1.2

// / 64KB is more than too much to send from a single plain-text file in all
// but some really far-out edge cases.  We'll live with those problems, such
// as a text file referenced in a SMIL 2.0 file with wordwrap param set to
// false but CRs and LFs treated literally, you could have 200000 bytes in
// line one followed by other text that should have displayed on line two (and
// would had the following limit been > 200000).  For PR 78150:
#define MAX_ALLOWED_PLAINTEXT_CHARS_TO_SEND 65535 

/****************************************************************************
 * 
 *  Function:
 * 
 *    HXCreateInstance()
 * 
 *  Purpose:
 * 
 *    Function implemented by all plugin DLL's to create an instance of 
 *    any of the objects supported by the DLL. This method is similar to 
 *    Window's CoCreateInstance() in its purpose, except that it only 
 *    creates objects from this plugin DLL.
 *
 *    NOTE: Aggregation is never used. Therefore and outer unknown is
 *    not passed to this function, and you do not need to code for this
 *    situation.
 * 
 */
STDAPI ENTRYPOINT(HXCreateInstance)
(
    IUnknown**  /*OUT*/ ppIUnknown
)
{
    *ppIUnknown = (IUnknown*)(IHXPlugin*)new CRealTextFileFormat();
    if (*ppIUnknown)
    {
      (*ppIUnknown)->AddRef();
      return HXR_OK;
    }
    return HXR_OUTOFMEMORY;
}

/****************************************************************************
 * 
 *  Function:
 * 
 *    CanUnload()
 * 
 *  Purpose:
 * 
 *    Function implemented by all plugin DLL's if it returns HXR_OK 
 *    then the pluginhandler can unload the DLL
 *
 */
STDAPI ENTRYPOINT(CanUnload)(void)
{
    return (g_nRefCount_rtff? HXR_FAIL : HXR_OK);
}


const char* CRealTextFileFormat::zm_pDescription    = "Helix RealText File Format Plugin";
const char* CRealTextFileFormat::zm_pCopyright      = HXVER_COPYRIGHT;
const char* CRealTextFileFormat::zm_pMoreInfoURL    = HXVER_MOREINFO;

const char* CRealTextFileFormat::zm_pFileMimeTypes[]  = 
      { "application/vnd.rn-realtext" /* <-- beta2 and beyond */,
        "text/vnd.rn-realtext" /* for incoming only */,
        "application/x-pn-realtext" /* <-- beta1 */,
          "text/plain",
          NULL};
const char* CRealTextFileFormat::zm_pFileExtensions[] = 
      {"rtx", "rt", "txt", NULL};  //+XXXEH- add htm and html here when ready.
///XXXEH- add *.txt as soon as it's correctly handled:
const char* CRealTextFileFormat::zm_pFileOpenNames[]  = 
      {"RealText File Format (*.rt)", NULL};


CRealTextFileFormat::CRealTextFileFormat()
    : m_lRefCount(0)
    , m_pContext(NULL)
    , m_pRegistry(NULL)
    , m_pErrorMessages(NULL)
    , m_bRealTextLicensed(FALSE)
    , m_pFileObject(NULL)
    , m_pFFResponse(NULL)
    , m_bHeaderSent(FALSE)
    , m_ulCurrentTime(0)
    , m_state(Ready)
    , m_pRequest(NULL)
    , m_ulNextPacketSeekPoint(0)
    , m_ulSequenceNum(0)
    , m_ulCurPacketSize(0)
    , m_ulHeaderSize(MAX_HEADER_SIZE)
    , m_ulSizeOfNextReadAfterSeek(0L)
    , m_ulLastByteParsedInFile(0L)
    , TextParser(&m_txtWin)
    , m_pCurTextLine(NULL)
    , m_pSavedDataFromLastRead(NULL)
    , m_bCurPacketHasREQUIREDContents(FALSE)
    ,   m_ulStartTimeOfTextOfPriorPacket(0L)
    , m_pFileStatObj(NULL)
    , m_ulTotalFileSizeInBytes(0L)
    ,   m_pszFileMimeType(NULL)
    , m_bIsTextPlainStreamMimeType(FALSE)

#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
    , m_logfile(0)
#endif
#endif
{
    g_nRefCount_rtff++;
};


CRealTextFileFormat::~CRealTextFileFormat()
{
    g_nRefCount_rtff--;
    //Killed mem leak with this:
    m_txtWin.reset(); //flushes all lists and stacks.
    
    Close();
}


/************************************************************************
 *  Method:
 *    IHXPlugin::InitPlugin
 *  Purpose:
 *    Initializes the plugin for use. This interface must always be
 *    called before any other method is called. This is primarily needed 
 *    so that the plugin can have access to the context for creation of
 *    IHXBuffers and IMalloc.
 */
STDMETHODIMP CRealTextFileFormat::InitPlugin(IUnknown* /*IN*/ pContext)
{
    INT32 nLicensed = 0;

    if (HXR_OK != pContext->QueryInterface(IID_IHXErrorMessages, 
                        (void**)&m_pErrorMessages))
    {
      return HXR_UNEXPECTED;
    }

    if (HXR_OK != pContext->QueryInterface(IID_IHXRegistry, 
                        (void**)&m_pRegistry))
    {
      return HXR_UNEXPECTED;
    }

    m_pContext = pContext;
    m_pContext->AddRef();

    // Figure out if RealText is licensed
    IHXPlayer* pPlayer = NULL;
    if (HXR_OK == m_pContext->QueryInterface(IID_IHXPlayer, (void**)&pPlayer))
    {
        // RealText is always licensed on the Player
        m_bRealTextLicensed = TRUE;
    }
    else
    {
        // On the Server, check the license section of the registry
        if (HXR_OK != m_pRegistry->GetIntByName(
            REGISTRY_REALTEXT_ENABLED, 
            nLicensed))
        {
          nLicensed = LICENSE_REALTEXT_ENABLED;
        }      
      m_bRealTextLicensed = (nLicensed) ? (TRUE) : (FALSE);
    }
    HX_RELEASE(pPlayer);

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXPlugin::GetPluginInfo
 *  Purpose:
 *    Returns the basic information about this plugin. Including:
 *
 *    bLoadMultiple     whether or not this plugin DLL can be loaded
 *                multiple times. All File Formats must set
 *                this value to TRUE.
 *    pDescription      which is used in about UIs (can be NULL)
 *    pCopyright  which is used in about UIs (can be NULL)
 *    pMoreInfoURL      which is used in about UIs (can be NULL)
 */
STDMETHODIMP CRealTextFileFormat::GetPluginInfo
(
    REF(BOOL)         /*OUT*/ bLoadMultiple,
    REF(const char*)/*OUT*/ pDescription,
    REF(const char*)/*OUT*/ pCopyright,
    REF(const char*)/*OUT*/ pMoreInfoURL,
    REF(ULONG32)    /*OUT*/ ulVersionNumber
)
{
    bLoadMultiple = TRUE;   // Must be true for file formats.

    pDescription    = zm_pDescription;
    pCopyright        = zm_pCopyright;
    pMoreInfoURL    = zm_pMoreInfoURL;
    ulVersionNumber = TARVER_ULONG32_VERSION;

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXPlugin::GetObjFileFormatInfo
 *  Purpose:
 *    If this object is a file format object this method returns
 *    information vital to the instantiation of file format plugins.
 *    If this object is not a file format object, it should return
 *    HXR_UNEXPECTED.
 */
STDMETHODIMP CRealTextFileFormat::GetFileFormatInfo
(
    REF(const char**) /*OUT*/ pFileMimeTypes,
    REF(const char**) /*OUT*/ pFileExtensions,
    REF(const char**) /*OUT*/ pFileOpenNames
)
{
    pFileMimeTypes  = zm_pFileMimeTypes;
    pFileExtensions = zm_pFileExtensions;
    pFileOpenNames  = zm_pFileOpenNames;

    return HXR_OK;
}

// *** IUnknown methods ***

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IUnknown::QueryInterface
//  Purpose:
//    Implement this to export the interfaces supported by your 
//    object.
//
STDMETHODIMP CRealTextFileFormat::QueryInterface(REFIID riid, void** ppvObj)
{
    if (IsEqualIID(riid, IID_IUnknown))
    {
      AddRef();
      *ppvObj = this;
      return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXPlugin))
    {
      AddRef();
      *ppvObj = (IHXPlugin*)this;
      return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXFileFormatObject))
    {
      AddRef();
      *ppvObj = (IHXFileFormatObject*)this;
      return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXFileResponse))
    {
      AddRef();
      *ppvObj = (IHXFileResponse*)this;
      return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXPendingStatus))
    {
      AddRef();
      *ppvObj = (IHXPendingStatus*)this;
      return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXInterruptSafe))
    {
      AddRef();
      *ppvObj = (IHXInterruptSafe*)this;
      return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXThreadSafeMethods))
    {
      AddRef();
      *ppvObj = (IHXThreadSafeMethods*)this;
      return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXFileMimeMapperResponse))
    {
      AddRef();
      *ppvObj = (IHXFileMimeMapperResponse*)this;
      return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXFileViewSource))
    {
      CRTViewSource* pVsrc = new CRTViewSource(m_pContext, 
          (IUnknown*)(IHXPlugin*)this);
      if ( pVsrc == NULL )
      {
          return HXR_FAIL;
      }
      return pVsrc->QueryInterface(riid, ppvObj);
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IUnknown::AddRef
//  Purpose:
//    Everyone usually implements this the same... feel free to use
//    this implementation.
//
STDMETHODIMP_(ULONG32) CRealTextFileFormat::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IUnknown::Release
//  Purpose:
//    Everyone usually implements this the same... feel free to use
//    this implementation.
//
STDMETHODIMP_(ULONG32) CRealTextFileFormat::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}

// *** IHXFileFormatObject methods ***

STDMETHODIMP CRealTextFileFormat::InitFileFormat
(
    IHXRequest*   /*IN*/      pRequest, 
    IHXFormatResponse*  /*IN*/      pFormatResponse,
    IHXFileObject*      /*IN*/  pFileObject
)
{
    m_pRequest    = pRequest;
    BOOL bIsBeta1Player = ::IsBeta1Player(m_pRequest);
    SetIsBeta1Player(bIsBeta1Player);

    m_pFFResponse = pFormatResponse;
    m_pFileObject = pFileObject;

    m_pRequest->AddRef();
    m_pFFResponse->AddRef();
    m_pFileObject->AddRef();

    // This file format is not a container type, so it only supports one
    // stream and therefore one header, but before we do that, we want to
    // make sure the file object is initialized; we can't actually return
    // the header count until the file init is done... (See InitDone).
    m_state = InitPending;

    // Note, we need to pass ourself to the FileObject, because this is its
    // first oppurtunity to know that we implement the IHXFileResponse
    // interface it will call for completed pending operations
    //killed this per Joe R and set the flag to HX_FILE_READ:
    // m_pFileObject->SetURL(m_pURL);
    return m_pFileObject->Init( HX_FILE_READ | HX_FILE_BINARY, this);
}     

STDMETHODIMP CRealTextFileFormat::Close()
{
    if (m_pContext) 
    {
      m_pContext->Release();
      m_pContext = 0;
    }

    if (m_pRegistry)
    {
      m_pRegistry->Release();
      m_pRegistry = 0;
    }

    if (m_pErrorMessages)
    {
      m_pErrorMessages->Release();
      m_pErrorMessages = 0;
    }

    if (m_pFileObject) 
    {
      m_pFileObject->Close();
      m_pFileObject->Release();
      m_pFileObject = 0;
    }

    if (m_pFFResponse) 
    {
      m_pFFResponse->Release();
      m_pFFResponse = 0;
    }

    if (m_pRequest)
    {
      m_pRequest->Release();
      m_pRequest = NULL;
    }

    if (m_pSavedDataFromLastRead)
    {
      m_pSavedDataFromLastRead->Release();
      m_pSavedDataFromLastRead = NULL;
    }

#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
    if(m_logfile)
    {
      if(m_txtWin.getDebugFlags()&RT_FF_DEBUG_FLAGS_MASK)
      {
          fprintf(m_logfile,"\nCRealTextFileFormat::Close()\n");
      }
      fclose(m_logfile);
      m_logfile=NULL;
    }
#endif
#endif

    HX_RELEASE(m_pFileStatObj);
    m_ulTotalFileSizeInBytes = 0L;

    HX_VECTOR_DELETE(m_pszFileMimeType);

    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IHXFileFormatObject::GetFileHeader
//  Purpose:
//    Called by controller to ask the file format for the number of
//    headers in the file. The file format should call the 
//    IHXFileFormatSession::HeaderCountReady() for the IHXFileFormat-
//    Session object that was passed in during initialization, when the
//    header count is available.
//
STDMETHODIMP CRealTextFileFormat::GetFileHeader()
{
    // If we are not ready then something has gone wrong
    if (m_state != Ready) return HXR_UNEXPECTED;

    // Since this is asyncronous we need to note our state so we can
    // correctly respond to the seek complete response from the file
    // object.
    m_state = GetFileHeaderSeekPending;

    // Actually seek...
    m_pFileObject->Seek(HEADER_OFFSET, FALSE);

    // See SeekDone() for next "step" of the GetFileHeader process.

    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IHXFileFormatObject::GetStreamHeader
//  Purpose:
//    Called by controller to ask the file format for the header for
//    a particular stream in the file. The file format should call 
//    IHXFileFormatSession::StreamHeaderReady() for IHXFileFormatSession
//    object that was passed in during initialization, when the header
//    is available.
//
STDMETHODIMP CRealTextFileFormat::GetStreamHeader(UINT16 unStreamNumber)
{
    // If we are not ready then something has gone wrong
    if (m_state != Ready) return HXR_UNEXPECTED;

    // If RealText is not licensed, log an error and return
    if (!m_bRealTextLicensed)
    {
        ReportError(IDS_ERR_RT_NOTLICENSED, HXR_NOT_LICENSED);
        m_pFFResponse->StreamHeaderReady(HXR_NOT_LICENSED, NULL);
        return HXR_OK;
    }

    // To give the header we need to be make sure the file is positioned
    // at the start of the file. We need to call the file object's
    // seek method.

    // Since this is asyncronous we need to note our state so we can
    // correctly respond to the seek complete response from the file
    // object.
    m_state = GetStreamHeaderSeekPending;

    // Actually seek...
    m_pFileObject->Seek(HEADER_OFFSET, FALSE);

    // See SeekDone() for next "step" of the GetStreamHeader process.

    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IHXFileFormatObject::GetPacket
//  Purpose:
//    Called by controller to ask the file format for the next packet
//    for a particular stream in the file. The file format should call 
//    IHXFileFormatSession::PacketReady() for the IHXFileFormatSession
//    object that was passed in during initialization, when the packet
//    is available.
//
STDMETHODIMP CRealTextFileFormat::GetPacket(UINT16 unStreamNumber)
{
    HX_RESULT result = HXR_OK;

    m_bCurPacketHasREQUIREDContents = FALSE;

    // If we are not ready then something has gone wrong
    if (m_state != Ready) return HXR_UNEXPECTED;

    if (!m_bHeaderSent)
    {
        result = HXR_UNEXPECTED;
    }
    else
    {
      // We are being asked to get the next packet and we have sent
      // the header, but we need to back up before we get the next
      // PACKET_SIZE bytes because the header varies in size and is
      // smaller than what was read.

      // Since this is asyncronous we need to note our state so we can
      // correctly respond to the seek complete response from the file
      // object.

      //If the next packet seek point is less than or equal to the last
      // byte parsed in the file, then we know we are seeking backwards
      // in time (and thus backwards into the file); the only exception
      // is if nothing has been parsed yet, i.e., last byte parsed==0 and
      // the next packet seek point is zero, i.e., there was no header
      // tag and the first text appears at byte zero.  (Logically, this
      // exception case is equivalent to one OR the other var being !=0):
      if(m_ulNextPacketSeekPoint <= m_ulLastByteParsedInFile  &&
            (m_ulLastByteParsedInFile  ||  m_ulNextPacketSeekPoint))
      {
          m_state = GetPacketStillBackedUpSeekPending;
          //Calculate start and end byte of next packet:
          ULONG32 ulPktEndByte;
          ULONG32 ul_retval = 0L;
          if(m_pTextWindow->m_pTLList)
          {
            ul_retval = m_pTextWindow->m_pTLList->
                  makeReasonableSizedPacketFromTextLinesAtStartByte(
                  m_ulNextPacketSeekPoint, &ulPktEndByte,
                  &m_bCurPacketHasREQUIREDContents,
                  &m_pCurTextLine);
          }

          HX_ASSERT(ul_retval); //this should never be zero here.
          if(!ul_retval)
          {
            //then there is nothing to send that's valid at time ulOffset
            // (or later since it gets later stuff if none is valid now),
            // so we've either read the whole file or need to read more:
            m_ulSizeOfNextReadAfterSeek = MAX_PACKET_SIZE;
            m_state = SeekSeekTooFarFwdPending;
            m_ulNextPacketSeekPoint = m_ulLastByteParsedInFile + 1L;
            m_pFileObject->Seek(m_ulNextPacketSeekPoint, FALSE);
          }
          else
          {
            // Note that the real time we are seeking to is not exactly
            // what was requested...
            //XXXEH- this should be set to the start time of pkt data
            // (which will always be less than or equal to ulOffset):
            HX_ASSERT_VALID_PTR(m_pCurTextLine);
            m_ulCurrentTime = (m_pCurTextLine!=NULL?
                  m_pCurTextLine->getStartTime() : m_ulCurrentTime);

            m_ulSizeOfNextReadAfterSeek =
                  1L + ulPktEndByte - m_ulNextPacketSeekPoint;

#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
            if(m_logfile  &&
                  m_txtWin.getDebugFlags()&RT_FF_DEBUG_FLAGS_MASK)
            {
                fprintf(m_logfile,
                      "GetPacket-Seek to %lu, "
                      "GetPacketStillBackedUpSeekPending\n",
                      m_ulNextPacketSeekPoint);
                fflush(m_logfile);
            }
#endif
#endif
            // Actually seek...
            m_pFileObject->Seek(HEADER_OFFSET+m_ulNextPacketSeekPoint,
                  FALSE);

            // See SeekDone() for next "step" of the Seek() process.
          }
      }
      else
      {
#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
            if(m_logfile  &&
                  m_txtWin.getDebugFlags()&RT_FF_DEBUG_FLAGS_MASK)
            {
                fprintf(m_logfile,
                      "GetPacket-Seek to %lu, GetPacketSeekPending\n",
                      HEADER_OFFSET+m_ulNextPacketSeekPoint);
                fflush(m_logfile);
            }
#endif
#endif
          m_state = GetPacketSeekPending;
          m_pFileObject->Seek(HEADER_OFFSET+m_ulNextPacketSeekPoint,FALSE);
      }


      // See SeekDone() for next "step" of the GetPacket process.
    }

    return result;
}


/////////////////////////////////////////////////////////////////////////
//  Method:
//    IHXFileFormatObject::Seek
//  Purpose:
//    Called by controller to tell the file format to seek to the 
//    nearest packet to the requested offset. The file format should 
//    call IHXFileFormatSession::SeekDone() for the IHXFileFormat-
//    Session object that was passed in during initialization, when 
//    the seek has completed.
//
STDMETHODIMP CRealTextFileFormat::Seek(ULONG32 ulOffset)
{
    // If we are not ready then something has gone wrong
    // if (m_state != Ready) return HXR_UNEXPECTED;
    /* This was wrong.  We can be waiting for a Read request to
     * complete when we get a Seek request.  In which case, we should
     * drop whatever we were waiting for, and assume the filesystem
     * will do the same when it gets our seek request.  
     */

    // Notice that the seek is passed as time in milliseconds, we
    // need to convert this to our packet number and its offset
    // in the file...

    // To actually seek to the correct place in the time line,
    // we will seek in the file. We need to call the file object's
    // seek method.


    // /Fixes PR 95127 by removing the plain-text's ignoring of this Seek();
    // if this is plain text, then go ahead and follow the same seek logic as
    // for RealText, noting that we're going to resend the whole file since
    // everything in the file has a begin of zero.

    // Since this is asyncronous we need to note our state so we can
    // correctly respond to the seek complete response from the file
    // object.
    m_state = SeekSeekPending;

    //Calculate start and end byte of next packet:
    ULONG32 ulPktStartByte, ulPktEndByte;
    ULONG32 ul_retval = 0L;

    m_ulStartTimeOfTextOfPriorPacket = 0L;

    if(m_pTextWindow->m_pTLList)
    {
      //XXXEH- change this function so it never makes a packet that's too
      // big; if  it just makes a reasonable-sized one and there are still
      // some TextLines that need to be sent, we'll get called back with
      // GetPacket() until the time stamp of the packet is greater than
      // the seek time, so it WILL all get sent:
      ul_retval = m_pTextWindow->m_pTLList->
              findBoundingStartAndEndBytesOfActiveTextLines(
            ulOffset, &ulPktStartByte, &ulPktEndByte, &m_pCurTextLine);
    }

    if(!ul_retval)
    {
      //then there is nothing to send that's valid at time ulOffset (or
      // later since it gets later stuff if none is valid now),
      // so we've either read the whole file or we need to read more:
      m_ulSizeOfNextReadAfterSeek = MAX_PACKET_SIZE;
        m_state = SeekSeekTooFarFwdPending;
      m_ulCurrentTime = ulOffset;
#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
      if(m_logfile  &&  m_txtWin.getDebugFlags()&RT_FF_DEBUG_FLAGS_MASK)
      {
          fprintf(m_logfile,"Seek fwd to byte %lu in file at time: %lu\n",
                m_ulLastByteParsedInFile+1L, m_ulCurrentTime);
          fflush(m_logfile);
      }
#endif
#endif
      m_pFileObject->Seek(m_ulLastByteParsedInFile + 1L, FALSE);
    }
    else
    {
      // Note that the real time we are seeking to is not exactly what
      // was requested...
      //XXXEH- this should be set to the start time of pkt data (which
      // will always be less than or equal to ulOffset):
      m_ulCurrentTime = ulOffset;

      m_ulSizeOfNextReadAfterSeek = 1L + ulPktEndByte - ulPktStartByte;

      // Actually seek...
      m_ulNextPacketSeekPoint = ulPktStartByte; //m_ulLastByteParsedInFile;
#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
      if(m_logfile  &&  m_txtWin.getDebugFlags()&RT_FF_DEBUG_FLAGS_MASK)
      {
          fprintf(m_logfile,"Seek back to byte %lu in file at time: %lu\n",
                m_ulNextPacketSeekPoint, m_ulCurrentTime);
          fflush(m_logfile);
      }
#endif
#endif
      m_pFileObject->Seek(m_ulNextPacketSeekPoint, FALSE);

      // See SeekDone() for next "step" of the Seek() process.
    }

    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IHXFileResponse::InitDone
//  Purpose:
//    Notification interface provided by users of the IHXFileObject
//    interface. This method is called by the IHXFileObject when the
//    initialization of the file is complete, and the Mime type is
//    available for the request file. If the URL is not valid for the
//    file system, the status HXR_FAILED should be returned,
//    with a mime type of NULL. If the URL is valid but the mime type
//    is unknown, then the status HXR_OK should be returned with
//    a mime type of NULL.
//
STDMETHODIMP CRealTextFileFormat::InitDone
(
    HX_RESULT     status
)
{
    // If we are not ready then something has gone wrong
    if (m_state != InitPending) return HXR_UNEXPECTED;

    // get a new file object for this file
    const char* pFilename;
    m_pFileObject->GetFilename(pFilename);
    m_RTSourceFileExtension = rtx_extension;
    if(pFilename)
    {
      UINT16 len = strlen(pFilename);
      if(len>3)  //5 is min length of acceptable file "a.rtx"
      {
          char* pNextDot = (char*)strchr(pFilename, '.');
          char* pLastDot = NULL;

          while(pNextDot)
          {
            pLastDot = pNextDot;
            pNextDot = (char*)strchr(++pNextDot, '.');
          }
          if(pLastDot)
          {
            if(!strncasecmp((const char*)pLastDot,".txt", 4))
            {
                m_RTSourceFileExtension = txt_extension;
                m_bIsTextPlainStreamMimeType = TRUE;
            }
            //".htm" or ".html" are handled by looking at first 4 chars:
            else if(!strncasecmp((const char*)pLastDot,".htm", 4))
            {
                m_RTSourceFileExtension = html_extension;
            }
            else
            {
                m_RTSourceFileExtension = rtx_extension;
            }
          }
      }
    }

    // Get an IHXFileStat interface from the IHXFileObject interface:
    HX_RELEASE(m_pFileStatObj);
    HX_ASSERT(m_pFileObject);
    HX_RESULT retVal = m_pFileObject->QueryInterface(IID_IHXFileStat,
            (void **) &m_pFileStatObj);    
    if (retVal != HXR_OK  ||  m_pFileStatObj == NULL)
    {
      m_state = Ready;
      return retVal; //we can't proceed if we can't see the file size!
    }

    // Call IHXFileStat::Stat() to get the size of the RT file:
    m_pFileStatObj->Stat((IHXFileStatResponse *) this);
    //See ::StatDone() for result of Stat() call.

    // XXXMEH - well, calling Stat() (an async method) in a 
    // synchronous way above is a no-no, so I'll the supposition
    // that two wrongs cancel each other out to call FindMimeType
    // in a synchronous way as well. When we change the above to add
    // another state for StatDone() before calling InitDone(), then
    // we should change this as well.
    IHXFileMimeMapper* pMapper = NULL;
    m_pFileObject->QueryInterface(IID_IHXFileMimeMapper, (void**) &pMapper);
    if (pMapper)
    {
        // Get the URL
        const char* pszURL = NULL;
        m_pRequest->GetURL(pszURL);
        if (pszURL)
        {
            // Get our own response interface
            IHXFileMimeMapperResponse* pResponse = NULL;
            QueryInterface(IID_IHXFileMimeMapperResponse, (void**) &pResponse);
            if (pResponse)
            {
                // Call FindMimeType - look in MimeTypeFound for
                // the response
                pMapper->FindMimeType(pszURL, pResponse);
            }
            HX_RELEASE(pResponse);
        }
    }
    HX_RELEASE(pMapper);


    // This file format is not a container type, so it only supports one
    // stream and therefore one header, since we now know the file is
    // initialized we can return the header count to the controller...
    m_state = Ready;

    m_pFFResponse->InitDone(status);

    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IHXFileResponse::CloseDone
//  Purpose:
//    Notification interface provided by users of the IHXFileObject
//    interface. This method is called by the IHXFileObject when the
//    close of the file is complete.
//
STDMETHODIMP CRealTextFileFormat::CloseDone(HX_RESULT status)
{
    return HXR_OK;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IHXFileResponse::ReadDone
//  Purpose:
//    Notification interface provided by users of the IHXFileObject
//    interface. This method is called by the IHXFileObject when the
//    last read from the file is complete and a buffer is available.
//
STDMETHODIMP CRealTextFileFormat::ReadDone
(
    HX_RESULT           status,
    IHXBuffer*          pBuffer
)
{
    HX_RESULT result = HXR_OK;

    // Note, the read may be done because we needed to read to produce
    // a header or a packet. We need to remember why we tried to read and
    // respond accordingly...

    switch (m_state)
    {
      case GetFileHeaderReadPending:
      {
          // We are in the process of responding to a GetFileHeader()
          // request and the read has completed, which means we
          // are done and can go back to the ready state...
          m_state = Ready;

          if (status == HXR_OK) //added this check
          {
            IHXValues* pHeader;
            IHXCommonClassFactory* pCommonClassFactory;
            if (HXR_OK == m_pContext->QueryInterface(
                  IID_IHXCommonClassFactory,
                  (void **)&pCommonClassFactory))
            {
                if(HXR_OK == pCommonClassFactory->CreateInstance(
                      CLSID_IHXValues,
                      (void**)&pHeader))
                {
                  SetDuration(DEFAULT_DURATION_MSEC);
                  char  szLoppedHdr[MAX_HEADER_SIZE+5] = ""; /* Flawfinder: ignore */

                  //Go through the pBuffer and find "duration=some val" 
                  // & set m_ulDuration to that value:
                  // and also look for version="x.y" in header:
                  ULONG32 bufSize = pBuffer->GetSize();
                  ULONG32 indx=0;
                  if(bufSize > MAX_HEADER_SIZE)
                  {     //this line should never get hit!:
                      bufSize = MAX_HEADER_SIZE;  
                  }
                  memcpy(szLoppedHdr, (char*)(pBuffer->GetBuffer()), /* Flawfinder: ignore */
                        bufSize);
                  szLoppedHdr[bufSize] = '\0';

                  // /If this is not plain text, then look for file header:
                  if (!m_bIsTextPlainStreamMimeType)
                  { 
                      //This parses the <window> tag and gets the duration,
                      // window width & height, ...etc. from it:
                      DealWithFileHeader(szLoppedHdr, bufSize);
 
                      _CHAR ch;
                      ULONG32 ulTmpDuration;
                      BOOL bEndTimeTagFound=FALSE;
                      m_ulHeaderSize = bufSize;
                      for (indx=0; indx<bufSize; indx++)
                      { 
                        ch = szLoppedHdr[indx];
 
                        if(!bEndTimeTagFound)
                        { 
                            if(' ' == ch  ||  '\t' == ch  ||
                                  '\n' == ch  ||  '\r' == ch)
                            { 
                              if (indx<bufSize-10)
                              { 
                                  _CHAR ch1 = szLoppedHdr[indx+1];
                                  //Look for "endtime" or "duration":
                                  if('E'==ch1  ||  'e'==ch1  ||
                                        'D'==ch1  ||  'd'==ch1)
                                  { 
                                    ulTmpDuration = GetEndTime(
                                          &szLoppedHdr[indx]);
                                    if(ulTmpDuration > 0L)
                                    { 
                                        SetDuration(ulTmpDuration);
                                        bEndTimeTagFound = TRUE;
                                    } 
                                  } 
                              } 
                            } 
                        } 
                        if('>' == szLoppedHdr[indx])
                        { 
                            m_ulHeaderSize = indx+1;
                            break;
                        } 
                      } 
                  } 

                  if(GetDuration() <= 0)
                  {
                      SetDuration(DEFAULT_DURATION_MSEC);
                  }

                  ULONG32 ulAvgBitRate = 0;
                  if (GetDuration() > 0)
                  {
                      ulAvgBitRate = (ULONG32)(
                        HEADER_OVERHEAD_FACTOR *
                        (double((8.0 * m_ulTotalFileSizeInBytes) /
                        ((double)GetDuration() / 1000.0))));
                  }
                  if(ulAvgBitRate < 8)
                      ulAvgBitRate = 9;
                  
                  pHeader->SetPropertyULONG32("AvgBitRate",
                        ulAvgBitRate);

                  pHeader->SetPropertyULONG32("StreamCount", 1);
                  pHeader->SetPropertyULONG32("IsRealDataType", 1);

                  //Set the .rt file's author-specified width & height
                  // (which may not have been explicitly specified; in
                  // that case the default value, which depends on the
                  // window type, is used):
                  pHeader->SetPropertyULONG32("Width", 
                        !m_bIsTextPlainStreamMimeType?
                        m_txtWin.getWidth() : DEFAULT_WINDOWWIDTH);
                  pHeader->SetPropertyULONG32("Height",
                        !m_bIsTextPlainStreamMimeType?
                        m_txtWin.getHeight() : DEFAULT_WINDOWHEIGHT);

                  // Tell the FormatResponse of our success in 
                  // getting the header.
                  m_pFFResponse->FileHeaderReady(HXR_OK, pHeader);

                  // Release our reference on the header!
                  HX_RELEASE(pHeader);
                }
                else
                {
                  return HXR_UNEXPECTED;
                }
                pCommonClassFactory->Release();
            }
            else
            {
                return HXR_UNEXPECTED;
            }
          }
          else
          {
#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
            if(m_logfile  &&
                  m_txtWin.getDebugFlags()&RT_FF_DEBUG_FLAGS_MASK)
            {
                fprintf(m_logfile,"FileHeader read failed.\n");
                fflush(m_logfile);
            }
#endif
#endif
            // Tell the FormatResponse that the read failed so we're
            // at the end of the file:
            m_pFFResponse->FileHeaderReady(HXR_INVALID_FILE, NULL);
          }
      }
      break;

      case GetStreamHeaderReadPending:
      {
          // We are in the process of responding to a GetStreamHeader()
          // request and the read has completed, which means we
          // are done and can go back to the ready state...
          m_state = Ready;

          if (status == HXR_OK) //added this check
          {
            // We now need to form a "header" object and pass it
            // off to our controller. Notice since our header data
            // comes straight out of the file as is, we don't need
            // to copy data at all, the IHXBuffer returned by read
            // can be placed into the header object and passed off.

            // Create header object here, notice that we call the
            // CreateInstance method of the controller, but we could
            // have implemented our own object that exposed the 
            // IRMAHeader interface.
            IHXValues* pHeader;
            IHXBuffer* pMTBuf;
            IHXBuffer* pSNBuf;
            IHXCommonClassFactory* pCommonClassFactory;
            if (HXR_OK == m_pContext->QueryInterface(
                  IID_IHXCommonClassFactory,
                  (void **)&pCommonClassFactory))
            {
                if((HXR_OK == pCommonClassFactory->CreateInstance(
                      CLSID_IHXValues,
                      (void**)&pHeader)) &&
                      (HXR_OK == pCommonClassFactory->CreateInstance(
                      CLSID_IHXBuffer,
                      (void**)&pMTBuf)) &&
                      (HXR_OK == pCommonClassFactory->CreateInstance(
                      CLSID_IHXBuffer,
                      (void**)&pSNBuf)))
                {
                  // Remember that we have sent the header so packets
                  // can now be sent... (just a little bit of
                  // sanity checking).
                  m_bHeaderSent = TRUE;

                  UINT16      uStreamNumber = 0;
                  //changed from float to double:
                  double      timePerPcktInSeconds =
                        double(TIME_PER_PACKET)/1000.0;
                  ULONG32     ulMaxBitRate = ULONG32(MAX_PACKET_SIZE *
                        (8.0*timePerPcktInSeconds));

                  ULONG32     ulMaxPacketSize = MAX_PACKET_SIZE;
                  ULONG32     ulAvgPacketSize = AVG_PACKET_SIZE;
                  ULONG32     ulStartTime = 0;
                  ULONG32     ulPreroll = DEFAULT_PREROLL_MSEC;
                  char  szStreamName[] = "RealText1";
                  char  szMimeType[64]; /* Flawfinder: ignore */
                  szMimeType[0] = '\0';
                  if(IsBeta1Player())
                  {
                      SafeStrCpy(szMimeType, zm_pFileMimeTypes[1], 64);
                  }
                  else
                  {
                      SafeStrCpy(szMimeType, zm_pFileMimeTypes[0], 64);
                  }
                        // If we have found that the mime type of the
                        // file is "text/plain", then we want to SEND
                        // "text/plain" as the STREAM mime type.
                        if (m_pszFileMimeType &&
                            0 == stricmp(m_pszFileMimeType, "text/plain"))
                        {
                      m_bIsTextPlainStreamMimeType = TRUE;
                            strcpy(szMimeType, m_pszFileMimeType); /* Flawfinder: ignore */
                        }
                  // /Handle .txt files as text/plain as well:
                  if (txt_extension == m_RTSourceFileExtension)
                  {
                      m_bIsTextPlainStreamMimeType = TRUE;
                            strcpy(szMimeType, "text/plain"); /* Flawfinder: ignore */
                  }

                  IHXBuffer*  pLoppedHdr = NULL;
                  char  szLoppedHdr[MAX_HEADER_SIZE+5] = ""; /* Flawfinder: ignore */

                  ULONG32 bufSize = pBuffer->GetSize();
                  ULONG32 indx=0;
                  //Go through the pBuffer and find first
                  // '>' (which should be the end of the <WINDOW ...>
                  // header tag), and send everything up to that point:
                  if(bufSize > MAX_HEADER_SIZE)
                  {     //this line should never get hit!:
                      bufSize = MAX_HEADER_SIZE;  
                  }
                  memcpy(szLoppedHdr, (char*)(pBuffer->GetBuffer()), /* Flawfinder: ignore */
                        bufSize);
                  szLoppedHdr[bufSize] = '\0';

                  //(Moved <window>-tag-handling code out and into
                  // GetFileHeaderReadPending handler, above. so that
                  // width and height could be passed out via file hdr)


#if OLD_SCHOOL_AVGBITRATE_CALCULATION
                  ULONG32     ulAvgBitRate = ulMaxBitRate;
#else
                  ULONG32 ulAvgBitRate = 0;
                  if (GetDuration() > 0)
                  {
                      ulAvgBitRate = (ULONG32)(
                        HEADER_OVERHEAD_FACTOR *
                        (double((8.0 * m_ulTotalFileSizeInBytes) /
                        ((double)GetDuration() / 1000.0))));
                  }
                  if(ulAvgBitRate < 8)
                      ulAvgBitRate = 9;
                  ulMaxBitRate = ulAvgBitRate * 2;
#endif

                  
                  ///XXXEH- Unfinished code:
                  ///If no '>' found, back up until the first '=' found
                  /// and then go forward until the the first
                  /// non-space/tab/newline char is found and then go
                  /// forward until the 1st space/tab/newline and then
                  /// put a '>' there and send that:
                  /// (For now, just do the following:)
                  /// Go to just past the first carriage return:
                  pCommonClassFactory->CreateInstance(
                            CLSID_IHXBuffer,(void**)&pLoppedHdr);
                  // /Only treat this as header if this is not plain text:
                  if (!m_bIsTextPlainStreamMimeType)
                  {
                      pLoppedHdr->Set(
                            (const UCHAR*)szLoppedHdr,m_ulHeaderSize+1);

                      // Fill in the Header with the relevant data...
                      pHeader->SetPropertyBuffer ("OpaqueData",
                            pLoppedHdr);
                  }
                  pHeader->SetPropertyULONG32("StreamNumber",
                        uStreamNumber);
#if OLD_SCHOOL_AVGBITRATE_CALCULATION
                  pHeader->SetPropertyULONG32("MaxBitRate",
                        9);//ulMaxBitRate);
                  pHeader->SetPropertyULONG32("AvgBitRate",
                        9);//ulAvgBitRate);
#else
                  pHeader->SetPropertyULONG32("MaxBitRate",
                        ulMaxBitRate);
                  pHeader->SetPropertyULONG32("AvgBitRate",
                        ulAvgBitRate);
#endif
                  pHeader->SetPropertyULONG32("MaxPacketSize",
                        9);//ulMaxPacketSize);
                  pHeader->SetPropertyULONG32("AvgPacketSize",
                        9);//ulAvgPacketSize);
                  pHeader->SetPropertyULONG32("StartTime",
                        ulStartTime);
                  pHeader->SetPropertyULONG32("Preroll",
                        ulPreroll);
                  pHeader->SetPropertyULONG32("Duration",
                        GetDuration());


                  // /Needed for fixing PR 78150:
                  pHeader->SetPropertyULONG32("SourceFileSize",
                        m_ulTotalFileSizeInBytes);
                  if (m_bIsTextPlainStreamMimeType)
                  {
                      pHeader->SetPropertyULONG32("MaxPlainTextBytesToBeSent",
                            MAX_ALLOWED_PLAINTEXT_CHARS_TO_SEND);
                  }


                  // /Treat .txt files as discrete media, but only when
                  // played in a SMIL 2.0 (or higher) presentation;
                  // we don't want to break old content so leave the
                  // 60000-millisec duration as is and let the SMIL
                  // renderer look for the intrinsicDurationType value:
                  IHXBuffer* pIntrDurTypeBuf = NULL;
                  if (HXR_OK == pCommonClassFactory->CreateInstance(
                        CLSID_IHXBuffer, (void**)&pIntrDurTypeBuf) )
                  {
                      HX_RESULT retVal = HXR_FAIL;
                      if (txt_extension == m_RTSourceFileExtension ||
                                0 == stricmp(szMimeType, "text/plain"))
                      {
                        HX_ASSERT(m_bIsTextPlainStreamMimeType);
                        retVal = pIntrDurTypeBuf->Set(
                              (const UCHAR *)
                              "intrinsicDurationDiscrete",
                              1 + strlen(
                              "intrinsicDurationDiscrete") );
                      }
                      else
                      {
                        retVal = pIntrDurTypeBuf->Set(
                              (const UCHAR *)
                              "intrinsicDurationContinuous",
                              1 + strlen(
                              "intrinsicDurationContinuous") );
                      }
                      if (HXR_OK == retVal)
                      {
                        pHeader->SetPropertyCString(
                            "intrinsicDurationType",pIntrDurTypeBuf);
                      }
                      HX_RELEASE(pIntrDurTypeBuf);
                  }
                  //The following is sent by the RT file format to the
                  // RT renderer to notify it what "parsing" version to
                  // use. This is done via the stream header properties
                  // "RTMarkupParsing[Major|Minor]Version".
                  // This must be done so that the ff and the renderer
                  // are in sync as far as how they deal with new tags,
                  // e.g., if the file format ignores a tag it doesn't
                  // recognize but the renderer is newer and recognizes
                  // the tag, the renderer should behave as does the ff
                  // and ignore the tag otherwise the renderer might
                  // display the text in a different place than the ff
                  // thinks it is and the next packet sent by the ff
                  // will tell the renderer to draw in a place that
                  // might overlap the prior packet's text due to this
                  // discrepancy.
                  pHeader->SetPropertyULONG32(
                        "RTMarkupParsingMajorVersion",
                        REAL_TEXT_MARKUP_PARSING_MAJOR_VERSION);
                  pHeader->SetPropertyULONG32(
                        "RTMarkupParsingMinorVersion",
                        REAL_TEXT_MARKUP_PARSING_MINOR_VERSION);

                  //Check if the content version is something that this
                  // file format recognizes:
                  BOOL bVersionIsRecognizedByThisFF = TRUE;
                  if((m_pTextWindow->getMajorContentVersion() >
                        REAL_TEXT_CONTENT_MAJOR_VERSION)  ||
                        (m_pTextWindow->getMajorContentVersion() ==
                        REAL_TEXT_CONTENT_MAJOR_VERSION  &&
                        m_pTextWindow->getMinorContentVersion() >
                        REAL_TEXT_CONTENT_MINOR_VERSION) )
                  {
                      bVersionIsRecognizedByThisFF = FALSE;
                  }

                  ULONG32 ulMinRTMajorVersion = 0L;
                  ULONG32 ulMinRTMinorVersion = 0L;
                  //Check if the content version is something that an
                  // older, pre-"version=..."-handling rtrender (which
                  // means any pre-April 1999 public release) can
                  // handle:
                  if((m_pTextWindow->getMajorContentVersion() >
                        REAL_TEXT_1ST_MAJ_VER_POST_VER_TAG)  ||
                        (m_pTextWindow->getMajorContentVersion() ==
                        REAL_TEXT_1ST_MAJ_VER_POST_VER_TAG  &&
                        m_pTextWindow->getMinorContentVersion() >=
                        REAL_TEXT_1ST_MNR_VER_POST_VER_TAG) )
                  {
                      if((ulMinRTMajorVersion <
                              REAL_TEXT_1ST_MAJ_VER_POST_VER_TAG)
                              ||
                              (REAL_TEXT_1ST_MAJ_VER_POST_VER_TAG==
                              ulMinRTMajorVersion  &&
                              ulMinRTMinorVersion <
                              REAL_TEXT_1ST_MNR_VER_POST_VER_TAG) )
                      {
                        //Use these to force auto-upgrade in the
                        // older rtrenders:
                        ulMinRTMajorVersion =
                              REAL_TEXT_1ST_MAJ_VER_POST_VER_TAG;
                        ulMinRTMinorVersion =
                              REAL_TEXT_1ST_MNR_VER_POST_VER_TAG;
                      }
                  }

                  //These (put together) are the minimum version that
                  // the renderer must know about.  If the renderer is
                  // older, it must fire auto-upgrade (but note: pre-
                  // Gold renderers (i.e., pre 11/23/1998 release)
                  // don't handle this and may render poorly if
                  // the following values are greater than 1.0:
                  // New note: all pre-gold renderers have expired
                  // as of 3/1999, so no worries.
                  pHeader->SetPropertyULONG32("MinRTMajorVersion",
                        ulMinRTMajorVersion);
                  pHeader->SetPropertyULONG32("MinRTMinorVersion",
                        ulMinRTMinorVersion);

                  //These (put together) are the version that the rt
                  // file's author has declared for the file contents.
                  // If omitted from the file, it is 0.0: Note: this
                  // allows us to properly deal with future changes to
                  // the header and other tags in the rt file, namely:
                  // force auto-upgrade of renderer:
                  pHeader->SetPropertyULONG32("RTMajorContentVersion",
                        m_pTextWindow->getMajorContentVersion() );
                  pHeader->SetPropertyULONG32("RTMinorContentVersion",
                        m_pTextWindow->getMinorContentVersion() );
    
                  pSNBuf->Set((const BYTE*)szStreamName,
                        strlen(szStreamName)+1);
                  pMTBuf->Set((const BYTE*)szMimeType,
                        strlen(szMimeType)+1);
                  pHeader->SetPropertyCString("StreamName",    pSNBuf);
                  pHeader->SetPropertyCString("MimeType",      pMTBuf);

                  IHXBuffer* pASM = 0;
                  char pBook[256]; /* Flawfinder: ignore */

#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
                  if(m_txtWin.getDebugFlags()&RT_FF_DEBUG_FLAGS_MASK &&
                        !m_logfile) //Only create log for 1st stream.
                  {
#if defined(_UNIX)  ||  defined(_WINDOWS)
                      char logfilename[32]; /* Flawfinder: ignore */
#endif
                      char slashChar = '\\';
#if defined(_UNIX)
                      slashChar = '/';
#endif
#if defined(_MACINTOSH)
                      fopen("Macintosh HD:RT Debug:rtff_0.log", "w");
#endif
#if defined(_UNIX)  ||  defined(_WINDOWS)
                      sprintf(logfilename,"c:%crtff_%u.log", slashChar, /* Flawfinder: ignore */
                            uStreamNumber);
                      m_logfile=fopen(logfilename,"w");
#endif

                      if(m_logfile)
                      {
                        fprintf(m_logfile,"Stream number %u\n",
                              uStreamNumber);
                        fprintf(m_logfile,"Max bit rate %lu\n",
                              ulMaxBitRate);
                        fprintf(m_logfile,"Avg bit rate %lu\n",
                              ulAvgBitRate);
                        fprintf(m_logfile,"Max packet size %lu\n",
                              ulMaxPacketSize);
                        fprintf(m_logfile,"Avg packet size %lu\n",
                              ulAvgPacketSize);
                        fprintf(m_logfile,"Start time %lu ms\n",
                              ulStartTime);
                        fprintf(m_logfile,"Preroll %lu ms\n",
                              ulPreroll);
                        fprintf(m_logfile,"Duration %lu ms\n",
                              GetDuration());
                        fprintf(m_logfile,"Stream name: %s\n",
                              szStreamName);
                        fprintf(m_logfile,"Header opaque data length: %lu\n",
                              pLoppedHdr->GetSize());
                        fprintf(m_logfile,"Header opaque data:{{{%s}}}\n",
                              pLoppedHdr->GetBuffer());

                        fflush(m_logfile);
                      }
                  }
#endif
#endif

                  //Priority=10 is for all packets that contain any
                  // text marked as <REQUIRED>.  Priority=9 is for all
                  // other packets:
                  sprintf(pBook, "priority=9,timestampdelivery=true; priority=10,timestampdelivery=true;"); /* Flawfinder: ignore */


                  if (HXR_OK == pCommonClassFactory->CreateInstance(
                        CLSID_IHXBuffer,
                        (void**)&pASM))
                  {
                      pASM->Set((const unsigned char *)pBook,
                            sizeof(pBook));
                      pHeader->SetPropertyCString("ASMRuleBook", pASM);
                      pASM->Release();
                  }

                  // Tell the FormatResponse of our success in 
                  // getting the header.
                  if(bVersionIsRecognizedByThisFF)
                  {
                      m_pFFResponse->StreamHeaderReady(status, pHeader);
                  }
                  else
                  {
                      IHXErrorMessages* pErrorMessages;
                      if (m_pContext  &&
                            HXR_OK != m_pContext->QueryInterface(
                            IID_IHXErrorMessages,
                            (void**)&pErrorMessages))
                      {
                        pErrorMessages = NULL;
                      }

                      if (pErrorMessages)
                      {
                        const char* pFilename = "[rt file]";
                        if(m_pFileObject)
                        {
                            m_pFileObject->GetFilename(pFilename);
                        }
                        char* pTmp;
                        pTmp = new char[256];
                        sprintf(pTmp,  /* Flawfinder: ignore */
                              "Version in \"%s\" is not recognized by installed"
                              " RealText file format", pFilename);
                        pErrorMessages->Report(HXLOG_ERR,
                              HXR_INVALID_VERSION,
                              0, (const char*) pTmp, NULL);
                        HX_RELEASE(pErrorMessages);
                        delete [] pTmp;
                      }

                      // Tell the FormatResponse that we found content
                      // we can't handle:
                      m_pFFResponse->StreamHeaderReady(
                            HXR_INVALID_VERSION, NULL);
                  }

                  // Release our reference on the header!
                  pHeader->Release();
                  pSNBuf->Release();
                  pMTBuf->Release();
                  pLoppedHdr->Release();
                }
                pCommonClassFactory->Release();
            }
          }
          else
          {
#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
            if(m_logfile  &&
                  m_txtWin.getDebugFlags()&RT_FF_DEBUG_FLAGS_MASK)
            {
                fprintf(m_logfile,"StreamHeader read failed.\n");
                fflush(m_logfile);
            }
#endif
#endif
            // Tell the FormatResponse that the read failed so we're
            // at the end of the file:
            m_pFFResponse->StreamHeaderReady(HXR_INVALID_FILE, NULL);
          }
      }
      break;

      case GetPacketSeekTooFarFwdReadPending:
      case GetPacketReadPending:
      {
          // We are in the process of responding to a GetPacket()
          // request and the read has completed, which means we
          // are done and can go back to the ready state...
          m_state = Ready;

          if (status == HXR_OK)
          {
            // We now need to form a "packet" object and pass it
            // off to our controller. Notice since our packet data
            // comes straight out of the file as is, we don't need
            // to copy data at all, the IHXBuffer returned by read
            // can be placed into the packet object and passed off.

            // Create packet object here, notice that we call the
            // CreateInstance method of the controller, but we could
            // have implemented our own object that exposed the IHXPacket
            // interface.
            IHXPacket* pPacket;
            IHXCommonClassFactory* pCommonClassFactory;
            if (HXR_OK == m_pContext->QueryInterface(
                  IID_IHXCommonClassFactory,
                  (void **)&pCommonClassFactory))
            {
                if (HXR_OK == pCommonClassFactory->
                        CreateInstance(CLSID_IHXPacket,
                        (void**)&pPacket))
                {
                  IHXBuffer* pLoppedPkt = NULL;
                  char szLoppedPkt[MAX_PACKET_SIZE+MAX_PKT_HDR_SZ+1] = /* Flawfinder: ignore */
                        { 0 };

                  // Go through the pBuffer from the end and look for
                  // a '<' or  a '>'; if a '>' comes first, send the 
                  // whole packet (since we're not inside a <..> tag.
                  // If a '<' comes first, send everything that comes
                  // before it.
                  ULONG32 ulBufSize = pBuffer->GetSize();
                  if(ulBufSize > MAX_PACKET_SIZE)
                  {   //this line should never get hit:
                      ulBufSize = MAX_PACKET_SIZE;
                  }
                  memcpy(szLoppedPkt, /* Flawfinder: ignore */
                        (char*)(pBuffer->GetBuffer()), ulBufSize);
                  szLoppedPkt[ulBufSize] = '\0';

                  //XXXEH-: how do we know what charset
                  // we're in if we haven't parsed it yet?  This is
                  // probably the reason HTML makes you declare the
                  // solo charset for the whole doc in the header...
                  //Added this DBCS-friendly version
                  // of the search code.  Now, the code has to search
                  // from the start of the buffer to find out where the
                  // last '<' and '>' are, if any:
                  ULONG32 indx;
                  LONG32 lHighestLessThanCharIndex = -1L;
                  LONG32 lHighestGreaterThanCharIndex = -1L;
                  LONG32 lHighestSpaceTabNewlineCharOutsideTagIndex =
                        -1L;
                  LONG32 lHighestBRorPtagStartIndx = -1L;
                  //for contiguous-BR-determining:
                  LONG32 lMostRecentBRorPTagStartIndx = -1L;
                  BOOL bIsInsideTag = FALSE;
                  BOOL bEndsOnALeadByteOfDualByteChar = FALSE;
                  for(indx=0L; indx<ulBufSize; indx++)
                  {
                      char ch = szLoppedPkt[indx];
                      if((UCHAR)ch >= DBCS_MIN_LEAD_BYTE_VAL)
                      {
                        indx++;
                        if(indx==ulBufSize)
                        {
                            //lead byte w/no trail byte, so back up:
                            bEndsOnALeadByteOfDualByteChar = TRUE;
                            break;
                        }
                        continue; //skip over DBCS chars. 
                      }
                      else if('>' == ch)
                      {
                        lHighestGreaterThanCharIndex = (LONG32)indx;
                        bIsInsideTag = FALSE;
                        continue;
                      }
                      else if('<' == ch)
                      {
                        lHighestLessThanCharIndex = (LONG32)indx;
                        bIsInsideTag = TRUE;
                        // just before a <BR> or <P> tag is a great
                        // place to end a packet:
                        if(indx+2<ulBufSize)
                        {
                            if(('P'==szLoppedPkt[indx+1]  ||
                                  'p'==szLoppedPkt[indx+1])  &&
                                  ('>'==szLoppedPkt[indx+2]) )
                            {
                              //keep track of
                              // the first one of the last group
                              // of one or more BR|P tags so that
                              // a packet never ends w/BR or P:
                              if(lHighestBRorPtagStartIndx<0  ||
                                  indx -
                                  lMostRecentBRorPTagStartIndx>4)
                              {
                                  lHighestBRorPtagStartIndx=indx;
                              }
                              lMostRecentBRorPTagStartIndx=indx;
                            }
                        }
                        if(indx+3<ulBufSize)
                        {
                            if(('B'==szLoppedPkt[indx+1]  ||
                                  'b'==szLoppedPkt[indx+1])  &&
                                  ('R'==szLoppedPkt[indx+2]  ||
                                  'r'==szLoppedPkt[indx+2])  &&
                                  ('>'==szLoppedPkt[indx+3]  ||
                                  //can be "<br>" or "<br/>":
                                  (indx+4<ulBufSize  &&
                                  '/'==szLoppedPkt[indx+3]  &&
                                  '>'==szLoppedPkt[indx+4]) )
                                  )
                            {
                              //keep track of
                              // the first one of the last group
                              // of one or more BR|P tags so that
                              // a packet never ends w/BR or P:
                              if(lHighestBRorPtagStartIndx<0  ||
                                  indx - 
                                  lMostRecentBRorPTagStartIndx>5)
                              {
                                  lHighestBRorPtagStartIndx=indx;
                              }
                              lMostRecentBRorPTagStartIndx=indx;
                            }
                        }
                        continue;
                      }
                      else if(('\n' == ch  ||  '\r' == ch  ||
                            ' '==ch  ||  '\t'==ch)  &&  !bIsInsideTag)
                      {
                        lHighestSpaceTabNewlineCharOutsideTagIndex =
                              (LONG32)indx;
                        continue;
                      }
                      //else is regular single-byte char, so continue.
                  }
                  BOOL bMinPktSzProb = FALSE;
                  if(bIsInsideTag) //data ends inside a tag "<..>":
                  {
                      if(MIN_PACKET_SIZE > lHighestLessThanCharIndex)
                      {
                        ///XXXEH- unfinished code: don't send less than
                        /// MIN_PACKET_SIZE, and, if you have to, 
                        /// send flag saying the packet is ending
                        /// inside a tag:
                        m_ulCurPacketSize = ulBufSize;//send it all.
                        bMinPktSzProb = TRUE;
                      }
                  }
                  if(!bMinPktSzProb) //(was "else")
                  {
                      // just before a <BR> or <P> tag is a great place
                      // to end a packet:
                      if((lHighestBRorPtagStartIndx>MIN_PACKET_SIZE) &&
                            (lHighestBRorPtagStartIndx
                            +10   //go w/earlier BR
                            > 
                         lHighestSpaceTabNewlineCharOutsideTagIndex))
                      {
                        //goes to just BEFORE the <BR>:
                        m_ulCurPacketSize = 
                              lHighestBRorPtagStartIndx;
                      }
                      else if(      
                        lHighestSpaceTabNewlineCharOutsideTagIndex >=
                            MIN_PACKET_SIZE)
                      {
                        m_ulCurPacketSize = 1 +  //add 1 to include it
                          lHighestSpaceTabNewlineCharOutsideTagIndex;
                      }
                      else if(
                            lHighestLessThanCharIndex >=
                            MIN_PACKET_SIZE  &&
                            lHighestLessThanCharIndex >=
                            lHighestGreaterThanCharIndex
                            )
                      {
                        m_ulCurPacketSize =//(don't +1 to include it)
                              lHighestLessThanCharIndex;
                      }
                      else if(      
                            lHighestGreaterThanCharIndex >=
                            MIN_PACKET_SIZE)
                      {
                        m_ulCurPacketSize = 1 + //add 1 to include it
                              lHighestGreaterThanCharIndex;
                      }
                      else //no way to split text except inside word
                      {
                        m_ulCurPacketSize = ulBufSize;//send it all. 
                        if (bEndsOnALeadByteOfDualByteChar  &&
                              // /Fixes PR 72843: if a high-128-
                              // valued byte ends the file, then
                              // we're done (so don't keep re-reading
                              // the last character of the file!!):
                              m_ulNextPacketSeekPoint + ulBufSize <
                              m_ulTotalFileSizeInBytes)
                        {   //back up to just before the lead byte:
                            m_ulCurPacketSize = ulBufSize-1;
                        }
                      }
                  }

                  szLoppedPkt[m_ulCurPacketSize] = '\0';

                  ULONG32 ulCurPktStartByteInFile =
                        m_ulNextPacketSeekPoint;
                  //Next read should be done where the first un-parsed
                  // data is in the file:
                  m_ulNextPacketSeekPoint += m_ulCurPacketSize;

                  //>>//Parse the data to determine
                  // start and end times of data in packet:
                  // (NOTE: don't need to add one for the NULL-
                  // terminating char because ParseText does not expect
                  // a NULL-terminated string)
                  ULONG32 ulStartTimeOfPacket = m_ulCurrentTime;
                  ULONG32 ulEndTimeOfPacket = m_ulCurrentTime;

                  // /Helps fix PR 82567:
                  // If this is plain text, we don't want to send
                  // anything but time-0 packets since the SMIL2 renderer
                  // will reset our dur to 1 millisec and that will cause
                  // any packets after that time to be treated as past
                  // the end of the stream:
                  if (m_bIsTextPlainStreamMimeType)
                  {
                      ulStartTimeOfPacket = ulEndTimeOfPacket = 0;
                  }

                  m_bCurPacketHasREQUIREDContents=FALSE;
                  //Note: ParseText messes with the text (by placing
                  // '\0's in it) so we need to restore the szLoppedPkt
                  // buffer after:
                  //XXXEH- fix ParseText so it doesn't alter the buffer
                  //ParseText returns the earliest start time of all
                  // data it parses into TextLine objects:
                  HX_RESULT hxrParseTextRetVal = ParseText(szLoppedPkt,
                        m_ulCurPacketSize,
                        0,  //XXXEH-this is for renderer only, right?
                        ulStartTimeOfPacket,
                        ulEndTimeOfPacket,
                        //This is passed by ref and, if ANY of the
                        // text in the szLoppedPkt is between a
                        // <REQUIRED> and a </REQUIRED> tag, then
                        // this value gets set to TRUE, else FALSE:
                        m_bCurPacketHasREQUIREDContents,
                        TRUE,//TRUE==is called from file format.
                        //This tells ParseText
                        // where we are in the file so each TextLine
                        // can keep track of its seek point:
                        ulCurPktStartByteInFile,
                        &m_pCurTextLine
                        );

                  // /Helps fix PR 82567: if this is played in a SMIL 2.0
                  // file then plain text packets were getting dropped in
                  // larger files if current time was greater than 1ms
                  // since duration was being reset by SMIL to 1 millisec:
                  if (m_bIsTextPlainStreamMimeType)
                  {
                      ulStartTimeOfPacket =  ulEndTimeOfPacket = 0;
                  }
                  else if (HXR_OK != hxrParseTextRetVal)
                  {   //XXXEH- should this situation be better handled?
                      ulStartTimeOfPacket = m_ulCurrentTime;
                      ulEndTimeOfPacket = m_ulCurrentTime;
                  }
                  else if(ulStartTimeOfPacket > GetDuration())
                  {
#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
                      if(m_logfile  &&
                            m_txtWin.getDebugFlags()&
                            RT_FF_DEBUG_FLAGS_MASK)
                      {
                        fprintf(m_logfile,"StreamDone() #2.\n");
                        fflush(m_logfile);
                      }
#endif
#endif
                      m_pFFResponse->StreamDone(0);
                      return result;
                  }

                  memcpy(szLoppedPkt, /* Flawfinder: ignore */
                        (char*)(pBuffer->GetBuffer()),
                        m_ulCurPacketSize);
                  szLoppedPkt[m_ulCurPacketSize] = '\0';
                  ULONG32 ulNumTCsDeleted = 
                        m_pTextWindow->deleteAllTCsUpToLastLine();



                  ULONG32 ulNumBytesParsedGoingInThisPkt =
                        m_ulCurPacketSize;
                  ULONG32 ulNumBytesParsedNotGoingInThisPkt = 0L;

#if defined(SAVE_PARTIAL_TEXTLINE_AT_END_OF_READ_DATA)
                  //Let's not send the final TL parsed because it
                  // may not be a complete line yet, so it'll have
                  // to wait until more data is parsed in the next
                  // GetPacket()-->...-->ReadDone() call:
                  TextLine* pTL_finalTLParsed = 
                        m_pTextWindow->m_pTLList->end();
                  ULONG32 ulStartByteOfLastTL =ulCurPktStartByteInFile;
                  if(pTL_finalTLParsed)
                  {
                      if(m_pCurTextLine)
                      {
                        //XXXEHFOO - test code if this "if" fails!!:
                        if(m_pCurTextLine->getStartByteInFile() <
                            pTL_finalTLParsed->getStartByteInFile())
                        {
                            //Only set this (to skip last TL) if it's
                            // not the only TL in the packet:
                            ulStartByteOfLastTL = pTL_finalTLParsed->
                                  getStartByteInFile();
                            ulNumBytesParsedGoingInThisPkt =
                                  ulStartByteOfLastTL -
                                  ulCurPktStartByteInFile;
                            ulNumBytesParsedNotGoingInThisPkt
                                  m_ulCurPacketSize -
                                  ulNumBytesParsedGoingInThisPkt;

                            if(!m_pSavedDataFromLastRead)
                            {
                              pCommonClassFactory->CreateInstance(
                                    CLSID_IHXBuffer,
                                    (void**)
                                    &m_pSavedDataFromLastRead);
                            }
                            m_pSavedDataFromLastRead->Set(
                                  (const UCHAR*)
                                  (szLoppedPkt+
                                  ulNumBytesParsedGoingInThisPkt),
                                  //add 1 for NULL char:
                                  ulNumBytesParsedNotGoingInThisPkt
                                  +1);
                        }
                      }
                  }
                  if(ulStartByteOfLastTL == ulCurPktStartByteInFile)
                  {
                      ulNumBytesParsedGoingInThisPkt=m_ulCurPacketSize;
                      ulNumBytesParsedNotGoingInThisPkt = 0L;
                      //If we didn't have any "leftovers" (i.e., no
                      // partial TextLine at the end) then clear the
                      // IHXBuffer so we don't use any old data:
                      if(m_pSavedDataFromLastRead)
                      {
                        m_pSavedDataFromLastRead->Release();
                        m_pSavedDataFromLastRead = NULL;
                      }
                  }
#endif



                  char pPacketHeaderBuf[MAX_PKT_HDR_SZ+1]; /* Flawfinder: ignore */
                  pPacketHeaderBuf[0] = '\0';
                  //This is the byte index relative to the cur buffer:
                  ULONG32 ulLocationInPktOfStartByteOfTextLine = 0L;
                  ULONG32 ulNumPktHdrBytes = 0L;
                  if (m_pCurTextLine  &&
                        // /No packet header if this is plain text:
                        !m_bIsTextPlainStreamMimeType)
                  {
                      ulNumPktHdrBytes = 
                            m_pCurTextLine->OutputPacketHeaderString(
                            //XXXEH- 0 is invalid data ID. OK as long
                            // as live is only user of this value:
                            0L,
                            &m_txtWin,
                            //state != GetPacketSeekBackReadPending:
                            FALSE,
                            //XXXEH-this must be pre-allocated but it
                            //would make more sense to use IRMBuffer:
                            pPacketHeaderBuf, MAX_PKT_HDR_SZ,
                            m_txtWin.m_pFontUndoTagList,
                            m_ulCurPacketSize
                            );

                      ulLocationInPktOfStartByteOfTextLine =
                            m_pCurTextLine->getStartByteInFile() -
                            ulCurPktStartByteInFile;

                      m_pCurTextLine = NULL;
                  }

                  //Now, adjust num bytes because we're not going to
                  // send all the markup tags that come just before the
                  // start of the first TextLine:
                  ulNumBytesParsedGoingInThisPkt -= 
                        ulLocationInPktOfStartByteOfTextLine;
#if defined(SAVE_PARTIAL_TEXTLINE_AT_END_OF_READ_DATA)
                  ULONG32 ulLenOfSavedDataFromLastRead = 0L;
                  if(m_pSavedDataFromLastRead)
                  {
                      ulLenOfSavedDataFromLastRead = 
                            m_pSavedDataFromLastRead->GetSize();
                  }
#endif
                  m_ulCurPacketSize =
#if defined(SAVE_PARTIAL_TEXTLINE_AT_END_OF_READ_DATA)
                        ulLenOfSavedDataFromLastRead +
#endif
                        ulNumPktHdrBytes +
                        ulNumBytesParsedGoingInThisPkt;
                  /*XXXEH- replaced this with the above line...
                  m_ulCurPacketSize += ulNumPktHdrBytes;
                  m_ulCurPacketSize -=
                        ulCurPktStartByteInFile-ulStartByteOfLastTL;
                  */
                  ULONG32 ulNonHeaderPartSize = m_ulCurPacketSize - 
                        ulNumPktHdrBytes;
                  
                  pCommonClassFactory->CreateInstance(CLSID_IHXBuffer,
                        (void**)&pLoppedPkt);
                  pLoppedPkt->SetSize(
                        //add 1 for NULL char:
                        m_ulCurPacketSize+1);

                  char* pTmpBuf =
                        (char*)(pLoppedPkt->GetBuffer());
                  pTmpBuf[0] = '\0';

                  //Skip all the markup tags in the packet that
                  // come before the first raw text, and insert in
                  // their place the pPacketHeaderBuf:
#if defined(SAVE_PARTIAL_TEXTLINE_AT_END_OF_READ_DATA)
                  HX_ASSERT(ulLenOfSavedDataFromLastRead>0?
                        m_pSavedDataFromLastRead!=NULL);
                  HX_ASSERT(strlen(pPacketHeaderBuf) ==
                        ulNumPktHdrBytes);
                  if(ulLenOfSavedDataFromLastRead)
                  {
                      memcpy(pTmpBuf,  /* Flawfinder: ignore */
                            m_pSavedDataFromLastRead->GetBuffer(),
                            ulLenOfSavedDataFromLastRead);
                  }
                  memcpy((pTmpBuf + ulLenOfSavedDataFromLastRead),  /* Flawfinder: ignore */
                        pPacketHeaderBuf,
                        ulNumPktHdrBytes);
                  memcpy((pTmpBuf + ulLenOfSavedDataFromLastRead + /* Flawfinder: ignore */
                        ulNumPktHdrBytes), 
                        (szLoppedPkt +
                        ulLocationInPktOfStartByteOfTextLine),
                        ulNonHeaderPartSize);
#else
                  strcpy(pTmpBuf, pPacketHeaderBuf); /* Flawfinder: ignore */
                  memcpy((pTmpBuf+ulNumPktHdrBytes),  /* Flawfinder: ignore */
                        (szLoppedPkt+
                        ulLocationInPktOfStartByteOfTextLine),
                        ulNonHeaderPartSize);
#endif
                  //Now, make sure packet data is NULL-terminated:
                  pTmpBuf[m_ulCurPacketSize] = '\0';

                  //Added this so seeks backwards
                  // don't reparse the data; subtract 1 because this is
                  // the last byte of what's been read, not the next
                  // byte to be read:
                  m_ulLastByteParsedInFile =m_ulNextPacketSeekPoint-1L;

                  ULONG32 ulSendTimeOfCurPacket = ulStartTimeOfPacket;

                  // /Helps fix PR 78150: if a really rediculously-huge
                  // .txt file is being handled, cut it off at some a
                  // lower rediculously-huge number of characters:
                  if (m_bIsTextPlainStreamMimeType  &&
                        m_ulLastByteParsedInFile >
                        MAX_ALLOWED_PLAINTEXT_CHARS_TO_SEND)
                  {
                      HX_ASSERT(m_ulLastByteParsedInFile <=
                            MAX_ALLOWED_PLAINTEXT_CHARS_TO_SEND);
                      // Tell the FormatResponse that we've sent more
                      // than enough of this file already:
                      m_pFFResponse->StreamDone(0);
                      goto doneWithPacket;
                  }

                  //Send this with the start time of the text of the
                  // prior packet in case it starts with centered text
                  // because the prior packet's last line won't get
                  // centered until the renderer knows it has received
                  // the entire line:
                  if(m_ulStartTimeOfTextOfPriorPacket <
                        ulSendTimeOfCurPacket)
                  {
                      ulSendTimeOfCurPacket =
                            m_ulStartTimeOfTextOfPriorPacket;
                  }
                  m_ulStartTimeOfTextOfPriorPacket =
                        ulStartTimeOfPacket;

                  // Fill in the Packet with the relevant data...
                  pPacket->Set(pLoppedPkt,
                        ulSendTimeOfCurPacket,
                        0,
                        HX_ASM_SWITCH_ON,
                        //RuleBook#1 (priority=10) for all packets
                        // that contain any text marked <REQUIRED>;
                        // RuleBook#0 (priority=9) is for all
                        // other packets:
                        m_bCurPacketHasREQUIREDContents>0);

            
                  if(ulEndTimeOfPacket < m_ulCurrentTime)
                  {
#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
                      if(m_logfile  &&
                            m_txtWin.getDebugFlags()&
                            RT_FF_DEBUG_FLAGS_MASK)
                      {
                        fprintf(m_logfile,"From ReadDone,"
                              " continue reading at byte# %lu;"
                              " last-read data's endtime=%lu,"
                              " cur time=%lu.\n",
                              m_ulNextPacketSeekPoint,
                              ulEndTimeOfPacket, 
                              m_ulCurrentTime);                   
                        fflush(m_logfile);
                      }
#endif
#endif
                      //too late to send this packet, so Read() some
                      // more until data that is valid at this time is
                      // found (or until EOF is reached):
                      m_state = GetPacketSeekPending;
                      m_pFileObject->Seek(HEADER_OFFSET +
                            m_ulNextPacketSeekPoint,FALSE);
                  }
                  else
                  {
#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
                      if(m_logfile  &&
                            m_txtWin.getDebugFlags()&
                            RT_FF_DEBUG_FLAGS_MASK)
                      {
                        fprintf(m_logfile,"called PacketReady() "
                              "of time %lu,"
                              " stream %i of size %ld,"
                              " pkt times=(%lu-%lu), cur time=%lu,"
                              " REQUIRED = %s."
                              "\nPacket contents:{{{%s}}}.\n",
                              ulSendTimeOfCurPacket,
                              pPacket->GetStreamNumber(),
                              pLoppedPkt->GetSize(),
                              ulStartTimeOfPacket,
                              ulEndTimeOfPacket,                              
                              m_ulCurrentTime, 
                              (m_bCurPacketHasREQUIREDContents?
                                    "TRUE":"FALSE"),
                              pLoppedPkt->GetBuffer() );
                        fflush(m_logfile);
                      }
#endif
#endif
                      // Tell the FormatResponse of our success in 
                      // getting the packet.
                      m_pFFResponse->PacketReady(status, pPacket);
                  }
doneWithPacket:
                  // Release our reference on the packet and buffer:
                  pPacket->Release();
                  pLoppedPkt->Release();
                }
                pCommonClassFactory->Release();
            }
          }
          // If the read failed then we will call PacketReady() with
          // a NULL packet!
          else
          {
#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
            if(m_logfile  &&
                  m_txtWin.getDebugFlags()&RT_FF_DEBUG_FLAGS_MASK)
            {
                fprintf(m_logfile,"StreamDone() #3.\n");
                fflush(m_logfile);
            }
#endif
#endif
            // Tell the FormatResponse that the read failed so we're
            // at the end of the file:
            m_pFFResponse->StreamDone(0);
          }
      }
      break; //end of "case GetPacketReadPending".

      case GetPacketSeekBackReadPending:
      {
          HandleGetSeekBackReadPending(status, pBuffer);
      }
      break; //end of "case GetPacketSeekBackReadPending".

      default:
      {
          result = HXR_UNEXPECTED;
      }
      break;
    }

    return result;
}

void CRealTextFileFormat::HandleGetSeekBackReadPending
(
    HX_RESULT           status,
    IHXBuffer*          pBuffer
)

{
    // We are in the process of responding to a Seek() (backward)
    // request and the read has completed, which means we
    // are done and can go back to the ready state...
    m_state = Ready;

    if (status == HXR_OK)
    {
      // We now need to form a "packet" object and pass it
      // off to our controller.
      IHXPacket* pPacket;
      IHXCommonClassFactory* pCommonClassFactory;
      if (HXR_OK == m_pContext->QueryInterface(
            IID_IHXCommonClassFactory,
            (void **)&pCommonClassFactory))
      {
          if (HXR_OK == pCommonClassFactory->
                  CreateInstance(CLSID_IHXPacket,
                  (void**)&pPacket))
          {
            IHXBuffer* pPktBuffNullTerminated = NULL;

            ULONG32 ulBufSize = pBuffer->GetSize();

            m_ulCurPacketSize = ulBufSize;

            //NOTE: m_ulCurrentTime was set in Seek() to the time
            // requested by the seek:
            ULONG32 ulStartTimeOfPacket = m_ulCurrentTime;
            
            if(ulStartTimeOfPacket > GetDuration())
            {
#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
                if(m_logfile  &&
                      m_txtWin.getDebugFlags()&
                      RT_FF_DEBUG_FLAGS_MASK)
                {
                  fprintf(m_logfile,"StreamDone() #4.\n");
                  fflush(m_logfile);
                }
#endif
#endif
                m_pFFResponse->StreamDone(0);

                HX_RELEASE(pPacket);
                HX_RELEASE(pCommonClassFactory);
                return;
            }


            char pPacketHeaderBuf[MAX_PKT_HDR_SZ+1]; /* Flawfinder: ignore */
            pPacketHeaderBuf[0] = '\0';
            ULONG32 ulNumPktHdrBytes = 0L;
            if (m_pCurTextLine  &&
                  // /No packet header if this is plain text:
                  !m_bIsTextPlainStreamMimeType)
            {
                ulNumPktHdrBytes = 
                      m_pCurTextLine->OutputPacketHeaderString(
                      //XXXEH- 0 is invalid data ID. OK as long
                      // as live is only user of this value:
                      0L,
                      &m_txtWin,
                      //state == GetPacketSeekBackReadPending:
                      TRUE,
                      //XXXEH-this must be pre-allocated but it
                      //would make more sense to use IRMBuffer:
                      pPacketHeaderBuf, MAX_PKT_HDR_SZ,
                      m_txtWin.m_pFontUndoTagList,
                      m_ulCurPacketSize
                      );
            }
            m_ulCurPacketSize += ulNumPktHdrBytes;
            
            pCommonClassFactory->CreateInstance(CLSID_IHXBuffer,
                  (void**)&pPktBuffNullTerminated);
            pPktBuffNullTerminated->SetSize(
                  //add 1 for NULL char:
                  m_ulCurPacketSize+1);

            char* pTmpBuf =
                  (char*)(pPktBuffNullTerminated->GetBuffer());
            strcpy(pTmpBuf, pPacketHeaderBuf); /* Flawfinder: ignore */
            memcpy((pTmpBuf+::strlen(pPacketHeaderBuf)),  /* Flawfinder: ignore */
                  pBuffer->GetBuffer(), pBuffer->GetSize());

            
            //Now, make sure packet data is NULL-terminated:
            pTmpBuf[m_ulCurPacketSize] = '\0';

            // ignore NULL-terminator (it was not added when
            // figuring m_ulCurPacketSize):
            m_ulNextPacketSeekPoint += ulBufSize; 

            ULONG32 ulSendTimeOfCurPacket = ulStartTimeOfPacket;
            //Send this with the start time of the text of the
            // prior packet in case it starts with centered text
            // because the prior packet's last line won't get
            // centered until the renderer knows it has received
            // the entire line:
            if(m_ulStartTimeOfTextOfPriorPacket < ulSendTimeOfCurPacket)
            {
                ulSendTimeOfCurPacket = m_ulStartTimeOfTextOfPriorPacket;
            }
            m_ulStartTimeOfTextOfPriorPacket = ulStartTimeOfPacket;

            // Fill in the Packet with the relevant data...
            pPacket->Set(pPktBuffNullTerminated,
                  ulSendTimeOfCurPacket,
                  0,
                  HX_ASM_SWITCH_ON,
                  //RuleBook#1 (priority=10) for all packets
                  // that contain any text marked <REQUIRED>;
                  // RuleBook#0 (priority=9) is for all
                  // other packets:
                  m_bCurPacketHasREQUIREDContents>0);

#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
            if(m_logfile  &&
                  m_txtWin.getDebugFlags()&RT_FF_DEBUG_FLAGS_MASK)
            {
                fprintf(m_logfile,"called PacketReady() of time %lu,"
                      " stream %i of size %ld,"
                      " pkt starttime=%lu, cur time=%lu,"
                      " REQUIRED = %s."
                      "\nPacket contents:{{{%s}}}.\n",
                      ulSendTimeOfCurPacket,
                      pPacket->GetStreamNumber(),
                      pPktBuffNullTerminated->GetSize(),
                      ulStartTimeOfPacket,
                      m_ulCurrentTime, 
                      (m_bCurPacketHasREQUIREDContents?
                            "TRUE":"FALSE"),
                      pPktBuffNullTerminated->GetBuffer() );
                fflush(m_logfile);
            }
#endif
#endif
            // Tell the FormatResponse of our success in 
            // getting the packet.
            m_pFFResponse->PacketReady(status, pPacket);


            // Release our reference on the packet and buffer:
            pPacket->Release();
            pPktBuffNullTerminated->Release();
          }
          pCommonClassFactory->Release();
      }
    }
    // If the read failed then we will call PacketReady() with
    // a NULL packet, so call StreamDone(0):
    else
    {
#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
      if(m_logfile  &&
            m_txtWin.getDebugFlags()&
            RT_FF_DEBUG_FLAGS_MASK)
      {
          fprintf(m_logfile,"StreamDone() #5.\n");
          fflush(m_logfile);
      }
#endif
#endif
      m_pFFResponse->StreamDone(0);
    }
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IHXFileResponse::WriteDone
//  Purpose:
//    Notification interface provided by users of the IHXFileObject
//    interface. This method is called by the IHXFileObject when the
//    last write to the file is complete.
//
STDMETHODIMP CRealTextFileFormat::WriteDone(HX_RESULT status)
{
    // We don't ever write, so we don't expect to get this...
    return HXR_UNEXPECTED;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//    IHXFileResponse::SeekDone
//  Purpose:
//    Notification interface provided by users of the IHXFileObject
//    interface. This method is called by the IHXFileObject when the
//    last seek in the file is complete.
//
STDMETHODIMP CRealTextFileFormat::SeekDone(HX_RESULT status)
{
    HX_RESULT result = HXR_OK;

    /* This may happen in HTTP streaming when the file system
     * is in still a seeking mode when the next seek is issued.
     * The file system will then call SeekDone with a status of 
     * HXR_CANCELLED for the pending seek.
     */
    if (status == HXR_CANCELLED)
    {
      return HXR_OK;
    }

    // Note, the seek may be done because we needed to seek to produce
    // a packet or because as a file format we were asked to seek to
    // a time position. We need to remember why we tried to seek and
    // respond accordingly...

    switch (m_state)
    {
      case GetStreamHeaderSeekPending:
      {
          // We are in the process of responding to a GetStreamHeader()
          // request and the seek has completed, so now we will try
          // to read the first HEADER_SIZE bytes... again, since 
          // this is asynchronous, we need to set up our state...
          m_state = GetStreamHeaderReadPending;

          // Actually read...
          m_pFileObject->Read(MAX_HEADER_SIZE);

          // See ReadDone() for next "step" of GetStreamHeader()
      }
      break;

      case GetFileHeaderSeekPending:
      {
          // We are in the process of responding to a GetStreamHeader()
          // request and the seek has completed, so now we will try
          // to read the first HEADER_SIZE bytes... again, since 
          // this is asynchronous, we need to set up our state...
          m_state = GetFileHeaderReadPending;

          // Actually read...
          m_pFileObject->Read(MAX_HEADER_SIZE);

          // See ReadDone() for next "step" of GetStreamHeader()
      }
      break;

      case GetPacketSeekPending:
      {
          // We are being asked to get the next packet and we have sent
          // the header, so we should read in the next MAX_PACKET_SIZE bytes.
          // Again, since read is asynchronous, we need to set up our state:
          m_state = GetPacketReadPending;

          // Actually read...
          m_pFileObject->Read(MAX_PACKET_SIZE);

          // See ReadDone() for next "step" of GetPacket()
      }
      break;

      //added this so we don't parse the data all over
      // again when we've seeked backwards and haven't yet returned to
      // where the un-read file data is:
      case GetPacketStillBackedUpSeekPending:
      {
          // We are being asked to get the next packet and we have sent
          // the header, so we should read in the next MAX_PACKET_SIZE bytes.
          // Again, since read is asynchronous, we need to set up our state:
          m_state = GetPacketSeekBackReadPending;

          // Actually read...
          m_pFileObject->Read(m_ulSizeOfNextReadAfterSeek);

          // See ReadDone() for next "step" of GetPacket()
      }
      break;

      case SeekSeekPending:
      {
          // We are in the process of responding to a Seek()
          // request and the seek has completed, so we need to read
          // the appropriate data in the file and then inform the
          // FormatResponse that we're done (at which point GetPacket()
          // is called).
///XXXXEH       m_state = GetPacketSeekBackReadPending;

          // Actually read...
///XXXXEH       m_pFileObject->Read(m_ulSizeOfNextReadAfterSeek);

          // See ReadDone() for next "step"

          m_state = Ready;

#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
          if(m_logfile  &&
                m_txtWin.getDebugFlags()&RT_FF_DEBUG_FLAGS_MASK)
          {
            fprintf(m_logfile,"calling SeekDone [SeekSeekPending]\n");
          }
#endif
#endif

          //XXXEH- this may need to be added to each TextLine so we know
          // the comment-tag count...(???); technically, no TextLine
          // should start inside a comment tag, so...
          m_pTextWindow->setInsideCommentTagNestCount(0L);
          
          // Tell the FormatResponse of our success in seeking.
          m_pFFResponse->SeekDone(status);

      }
      break;

      //This handles the case where we need to read more
      // data because seek occurred & needs to go beyond what we've parsed:
      case SeekSeekTooFarFwdPending:
      {
///XXXXEH       m_state = GetPacketSeekTooFarFwdReadPending;

    // Actually read...
///XXXXEH       m_pFileObject->Read(MAX_PACKET_SIZE);

          // See ReadDone() for next "step"

          m_state = Ready;

#ifdef _DEBUG
#if defined(RT_OUTPUT_LOGFILE)
          if(m_logfile  &&
                m_txtWin.getDebugFlags()&RT_FF_DEBUG_FLAGS_MASK)
          {
            fprintf(m_logfile,
                  "calling SeekDone [SeekSeekTooFarFwdPending]\n");
          }
#endif
#endif

          // Tell the FormatResponse of our success in seeking.
          m_pFFResponse->SeekDone(status);
      }
      break;

      default:
      {
          result = HXR_UNEXPECTED;
      }
      break;
    }

    return result;
}

/************************************************************************
 *    Method:
 *        IHXFileResponse::FileObjectReady
 *    Purpose:
 *        Notification interface provided by users of the IHXFileObject
 *        interface. This method is called by the IHXFileObject when the
 *        requested FileObject is ready. It may return NULL with 
 *        HX_RESULT_FAIL if the requested filename did not exist in the 
 *        same pool.
 */
STDMETHODIMP 
CRealTextFileFormat::FileObjectReady            
(
    HX_RESULT status,
    IHXFileObject* pFileObject)
{
    return HXR_OK;
}

/************************************************************************
 *    Method:
 *        IHXPendingStatus::GetStatus
 *    Purpose:
 *        Called by the user to get the current pending status from an object
 */
STDMETHODIMP
CRealTextFileFormat::GetStatus
(
    REF(UINT16) uStatusCode, 
    REF(IHXBuffer*) pStatusDesc, 
    REF(UINT16) ulPercentDone
)
{
    HX_RESULT hResult = HXR_OK;
    IHXPendingStatus* pFileSystemStatus = NULL;

    // asking status information from the file system object
    if (m_pFileObject)
    {
      if (HXR_OK == m_pFileObject->QueryInterface(IID_IHXPendingStatus,(void**)&pFileSystemStatus))
      {
          hResult = pFileSystemStatus->GetStatus(uStatusCode, pStatusDesc, ulPercentDone);

          pFileSystemStatus->Release();
          return hResult;
      }
    }

    // by default
    uStatusCode = HX_STATUS_READY;
    pStatusDesc = NULL;
    ulPercentDone = 0;
 
    return hResult;
}


/////////////////////////////////////////////////////////////////////////
//  Method:
//    IHXFileFormatObject::StatDone
//
STDMETHODIMP
CRealTextFileFormat::StatDone(HX_RESULT status, UINT32 ulSize,
                              ULONG32 ulCreationTime,
                                      UINT32 ulAccessTime,
                              UINT32 ulModificationTime,
                              UINT32 ulMode)
{
    // We're finished with the IHXFileStat interface, so we'll release it:
    HX_RELEASE(m_pFileStatObj);

    // Now we know how big the .avi file is, so we can calculate the
    // total average bit rate for all streams by dividing
    // m_ulTotalFileSizeInBytes by duration:
    m_ulTotalFileSizeInBytes = ulSize;

    // Check for input error conditions:
    if (status != HXR_OK)
    {
        return status;
    }

    return HXR_OK;
}

void CRealTextFileFormat::ReportError(UINT32 ulErrorID, HX_RESULT retVal)
{
    // Try to get the string from the resource manager
    CHXString cErrStr;
    HX_RESULT errRet = GetResourceErrorString(ulErrorID, cErrStr);
    if (errRet != HXR_OK)
    {
        switch (ulErrorID)
        {
            case IDS_ERR_RT_NOTLICENSED:
                cErrStr = ERRSTR_RT_NOTLICENSED;
                break;
            default:
                cErrStr = ERRSTR_RT_GENERALERROR;
                break;
        }
    }
 
    if (m_pErrorMessages)
    {
        m_pErrorMessages->Report(HXLOG_CRIT, retVal, 0, (const char*) cErrStr,  NULL);
    }
}

HX_RESULT CRealTextFileFormat::GetResourceErrorString(UINT32 ulErrorID, CHXString& rErrorStr)
{
    IHXExternalResourceManager* pResMgr = NULL;
    HX_RESULT retVal = m_pContext->QueryInterface(IID_IHXExternalResourceManager, (void**) &pResMgr);
    if (retVal != HXR_OK)
    {
        return retVal;
    }

      IHXExternalResourceReader* pResRdr = NULL;
    retVal = pResMgr->CreateExternalResourceReader(CORE_RESOURCE_SHORT_NAME, pResRdr);
    if (retVal != HXR_OK)
    {
        HX_RELEASE(pResMgr);
        return retVal;
    }

    IHXXResource* pRes = pResRdr->GetResource(HX_RT_STRING, ulErrorID);
    if(!pRes)
    {
        HX_RELEASE(pResRdr);
        HX_RELEASE(pResMgr);
        return HXR_FAIL;
    }

    // Assign the error string to the out parameter
    rErrorStr = (const char*) pRes->ResourceData();

    // Release all references
    HX_RELEASE(pRes);
    HX_RELEASE(pResRdr);
    HX_RELEASE(pResMgr);

    return HXR_OK;
}



HX_RESULT CRealTextFileFormat::DealWithFileHeader(char*     szLoppedHdr,
      ULONG32 bufSize)
{
    _CHAR ch;
    ULONG32 indx = 0L;

    if (MAX_HEADER_SIZE < bufSize)
    {
      bufSize = MAX_HEADER_SIZE;
    }

    m_ulHeaderSize=bufSize;
    for (indx=0; indx<bufSize;indx++)
    {
      ch = szLoppedHdr[indx];

/*XXXEH- for now, we have to assume that the first text encountered is not
DBCS text; it and all text inside tags must be us-ascii charset:
      //Added for multibyte char support
      if((UCHAR)ch >= DBCS_MIN_LEAD_BYTE_VAL)
      {
          if (indx+1 >= bufSize)
          {
            //ignore:no trail byte follows:                       
            m_ulHeaderSize = indx;
            break;
          }
          indx++; //add extra byte to skip trail byte.
          continue;
      }
*/
      if('>' == szLoppedHdr[indx])
      {
          m_ulHeaderSize = indx+1;
          break;
      }
    }
    _CHAR savedCh = szLoppedHdr[indx+1];
    szLoppedHdr[indx+1] = '\0';
    ULONG32 ulEndOfHeaderStr = indx+1;
    
    if(GetDuration() <= 0)
    {
      SetDuration(DEFAULT_DURATION_MSEC);
    }

    //ParseHeader() parses the <WINDOW ..> tag and puts
    // all the values it finds into the m_pTextWindow of
    // *this::TextParser:
    if(!ParseHeader(szLoppedHdr, m_ulHeaderSize,
          REAL_TEXT_MARKUP_PARSING_MAJOR_VERSION,
          REAL_TEXT_MARKUP_PARSING_MINOR_VERSION))
    {
      //if 0 was returned, then no header tag was found
      // so treat this as a "plaintext" file and stream
      // from byte 0:
      indx=0;
      ulEndOfHeaderStr=0L;
    }
    else
    {
      if('>' == szLoppedHdr[indx])
      {
          indx++;
      }
      if('\0' == szLoppedHdr[indx])
      {
          szLoppedHdr[indx] = savedCh;
      }
    }
    //Now go to 1st character (after any leading spaces,
    // tabs, line breaks):
    for (; indx<bufSize; indx++)
    { 
      if('\n' == szLoppedHdr[indx]  ||  
            '\r' == szLoppedHdr[indx])
      {
          //Added the following to
          // skip multiple contiguous line breaks:
          for (; indx<bufSize; indx++)
          { 
            if('\n' != szLoppedHdr[indx]  &&
                  '\r' != szLoppedHdr[indx])
            {
                if(indx>0)
                {
                  indx--;  //(includes this char.)
                }
                break;
            }
          }
          break;
      }
      //In case the first char found (after all
      // contiguous space and tab chars) is not a
      // newline char, begin streaming data there:
      else
      {
          if(' ' != szLoppedHdr[indx]  &&
                '\t' != szLoppedHdr[indx])
          {
            if(indx>0)
            {
                indx--; //(includes this char.)
            }
            break;
          }
      }
    }
/*XXXEH- for now, we have to assume that the first text encountered is not
DBCS text; it and all text inside tags must be us-ascii charset:
    //Added for multibyte char support:
    if (bufSize==indx)
    {
      if((UCHAR)szLoppedHdr[indx] >=
            DBCS_MIN_LEAD_BYTE_VAL)
      {
          indx--;//back up: skip lead byte w/no trail
      }
    }
*/
    //NULL-terminate the header string right after the
    // ending '>' char:
    if(ulEndOfHeaderStr)
    {
      szLoppedHdr[ulEndOfHeaderStr] = '\0';
    }

    if(indx)
    {
      m_ulNextPacketSeekPoint = indx+1L;
    }
    else
    {
      m_ulNextPacketSeekPoint = 0L;
      m_ulHeaderSize = sprintf(szLoppedHdr, /* Flawfinder: ignore */
            "<WINDOW type=plaintext>");
    }

    return HXR_OK; 
} 

/************************************************************************
 *    Method:
 *        IHXThreadSafeMethods::IsThreadSafe
 */
STDMETHODIMP_(UINT32)
CRealTextFileFormat::IsThreadSafe()
{
    return HX_THREADSAFE_METHOD_FF_GETPACKET |
      HX_THREADSAFE_METHOD_FSR_READDONE;
}

STDMETHODIMP CRealTextFileFormat::MimeTypeFound(HX_RESULT   status,
                                                const char* pMimeType)
{
    HX_RESULT retVal = HXR_OK;

    if (SUCCEEDED(status) && pMimeType)
    {
        HX_VECTOR_DELETE(m_pszFileMimeType);
        m_pszFileMimeType = new char [strlen(pMimeType) + 1];
        if (m_pszFileMimeType)
        {
            strcpy(m_pszFileMimeType, pMimeType); /* Flawfinder: ignore */
        }
    }

    return retVal;
}

Generated by  Doxygen 1.6.0   Back to index