/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: rtffplin.cpp,v 1.1.2.1.4.1 2005/04/28 22:27:38 ehodge 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];
				SafeSprintf(pTmp, 256,  /* 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;
}
