/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: memfsys.cpp,v 1.3.38.2 2004/07/12 23:05:02 sehancher 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 ***** */

/////////////////////////////////////////////////////////////////////////////
// 
//  Memory File System for chunky res objects
//
//  This is a memory file system, it stores data in chunky res objects and
//  allows access to these objects as if they were files
//

#define INITGUID    1

#include "hxcom.h"
#include "hxtypes.h"
#include "memfsys.ver"

#include "hxcomm.h"
#include "ihxpckts.h"
#include "hxfiles.h"
#include "hxplugn.h"
#include "hxengin.h"
#include "hxcore.h"
#include "hxprefs.h"
#include "hxrendr.h"
#include "hxauth.h"
#include "hxauthn.h"
#include "hxplgns.h"
#include "hxpends.h"

#undef INITGUID

#include "hxathsp.h"
#include "hxcorsp.h"
#include "hxpktsp.h"
#include "hxcomsp.h"
#include "hxplnsp.h"

#include "timeval.h"
#include "dbcs.h"	/* for HXCompareStrings */
#include "hxstring.h"	/* for CHXString */
#include "hxurl.h"	/* for CHXURL */
#include "hxxfile.h"	/* for HXXFile::GetReasonableLocalFileName() */
#include "hxstrutl.h"
#include "hxver.h"
#include "chxpckts.h"
#include "hxslist.h"
#include "chunkres.h"

#include <stdio.h>
#include <string.h>

#ifdef _MACINTOSH
#include <fcntl.h>
#include "chxdataf.h"	// Macintosh file i/o
#include "macasyncfile.h"  // Macintosh interrupt file i/o
#elif (defined (_WINDOWS ) || defined (_WIN32)) && !defined(WIN32_PLATFORM_PSPC)
#include <direct.h>
#else
#include "hlxclib/sys/types.h"
#include "hlxclib/sys/stat.h"
#endif
#include "findfile.h"
#include "mfsiface.h"
#include "memfsys.h"

#if defined (_WINDOWS ) || defined (_WIN32)
#define OS_SEPARATOR_CHAR	'\\'
#define OS_SEPARATOR_STRING	"\\"
#elif defined (_UNIX)
#define OS_SEPARATOR_CHAR	'/'
#define OS_SEPARATOR_STRING	"/"
#elif defined (__MWERKS__)
#define OS_SEPARATOR_CHAR	':'
#define OS_SEPARATOR_STRING	":"
#endif // defined (_WINDOWS ) || defined (WIN32)

#ifdef _WIN16
#define MAX_RECURSION_LEVEL	10
#elif _LINUX
#define MAX_RECURSION_LEVEL	30
#else
#define MAX_RECURSION_LEVEL	200
#endif

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

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

INT32 g_nMemFSysRefCount = 0;

const char* CMemoryFileSystem::zm_pDescription	= "RealNetworks Memory File System";
const char* CMemoryFileSystem::zm_pCopyright	= HXVER_COPYRIGHT;
const char* CMemoryFileSystem::zm_pMoreInfoURL	= HXVER_MOREINFO;
const char* CMemoryFileSystem::zm_pShortName	= "pn-memory";
const char* CMemoryFileSystem::zm_pProtocol	= "mem";

IHXMemoryFileContext* CMemoryFileSystem::z_pMemCtx = NULL;

CChunkyResMgr*	    g_pChunkyResMgr	    = NULL;
CHXMapPtrToPtr*	    g_pMapChunkyToStatus    = NULL;
CHXMapPtrToPtr*	    g_pMapStreamToChunky    = NULL;
CHXMapStringToOb*  g_pMapURLToResponse	    = NULL;

UINT32 CMemoryFileSystem::z_uMaxRecursionLevel = MAX_RECURSION_LEVEL;



/****************************************************************************
 * 
 *  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
)
{
    if (!g_pChunkyResMgr)
    {
	g_pChunkyResMgr = new CChunkyResMgr;
	if (!g_pChunkyResMgr)
	{
	    return HXR_OUTOFMEMORY;
	}
    }

    if (!g_pMapChunkyToStatus)
    {
	g_pMapChunkyToStatus = new CHXMapPtrToPtr;
	if (!g_pMapChunkyToStatus)
	{
	    return HXR_OUTOFMEMORY;
	}
    }

    if (!g_pMapStreamToChunky)
    {
	g_pMapStreamToChunky = new CHXMapPtrToPtr;
	if (!g_pMapStreamToChunky)
	{
	    return HXR_OUTOFMEMORY;
	}
    }

    if (!g_pMapURLToResponse)
    {
	g_pMapURLToResponse = new CHXMapStringToOb;
	if (!g_pMapURLToResponse)
	{
	    return HXR_OUTOFMEMORY;
	}
    }

    *ppIUnknown = (IUnknown*)(IHXPlugin*) new CMemoryFileSystem;
    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 CanUnload(void)
{
    return (g_nMemFSysRefCount ? HXR_FAIL : HXR_OK);
}


/****************************************************************************
 * 
 *  Function:
 * 
 *	HXShutdown()
 * 
 *  Purpose:
 * 
 *	Function implemented by all plugin DLL's to free any *global* 
 *	resources. This method is called just before the DLL is unloaded.
 *
 */
STDAPI HXShutdown(void)
{
    HX_ASSERT(!g_nMemFSysRefCount);
    POSITION p;

    HX_RELEASE(CMemoryFileSystem::z_pMemCtx);

    if (g_pChunkyResMgr)
    {
	delete g_pChunkyResMgr;
	g_pChunkyResMgr = NULL;
    }

    if (g_pMapStreamToChunky)
    {
	delete g_pMapStreamToChunky;
	g_pMapStreamToChunky = NULL;
    }

    if (g_pMapChunkyToStatus)
    {
	CMemoryFileStatus* pStatus;
	CChunkyRes* pRes;
	p = g_pMapChunkyToStatus->GetStartPosition();
	while (p)
	{
	    g_pMapChunkyToStatus->GetNextAssoc(p, (void*&)pRes, (void*&)pStatus);
	    HX_ASSERT(pStatus);
	    HX_RELEASE(pStatus);
	}

	delete g_pMapChunkyToStatus;
	g_pMapChunkyToStatus = NULL;
    }

    if (g_pMapURLToResponse)
    {
	CHXString sURL;
	IHXFileResponse* pResponse;

	p = g_pMapURLToResponse->GetStartPosition();
	while (p)
	{
	    g_pMapURLToResponse->GetNextAssoc(p, sURL, (void*&)pResponse);
	    HX_ASSERT(pResponse);
	    pResponse->InitDone(HXR_FAIL);
	    HX_RELEASE(pResponse);
	}

	delete g_pMapURLToResponse;
	g_pMapURLToResponse = NULL;
    }

    return HXR_OK;
}



CMemoryFileStatus::CMemoryFileStatus(IHXMemoryFileContext* pContext, 
				     void* pID,
				     const char* pMime)
    : m_pContext(pContext)
    , m_pID(pID)
    , m_ulSize(0)
    , m_ulContentSize(0)
    , m_bDone(FALSE)
    , m_pMime(NULL)
    , m_lRefCount(0)
{
    HX_ASSERT(m_pContext);
    m_pContext->AddRef();

    SetMime(pMime);
}


CMemoryFileStatus::~CMemoryFileStatus()
{
    HX_RELEASE(m_pContext);

    if (m_pMime)
    {
	delete[] m_pMime;
    }
}


void CMemoryFileStatus::SetMime(const char* pMime)
{
    if (m_pMime)
    {
	delete[] m_pMime;
	m_pMime = NULL;
    }

    if (pMime && *pMime)
    {
	m_pMime = new char[strlen(pMime)+1];
	if (m_pMime)
	{
	    strcpy(m_pMime, pMime); /* Flawfinder: ignore */
	}
    }
}



LONG32 CMemoryFileStatus::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}


LONG32 CMemoryFileStatus::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}


void CMemoryFileStatus::SetDone(BOOL bDone)
{ 
    m_bDone = bDone;

    // Someone aborted this thing!
    if (m_ulSize < m_ulContentSize)
    {
	m_ulContentSize = m_ulSize;
    }
}



IHXMemoryFileContext* CMemoryFileStatus::GetContext()
{
    m_pContext->AddRef();
    return m_pContext;
}



CMemoryFileSystem::CMemoryFileSystem()
    : m_lRefCount(0)
    , m_pContext(0)
    , m_options(NULL)
{
    g_nMemFSysRefCount++;
}

CMemoryFileSystem::~CMemoryFileSystem()
{
    HX_ASSERT(g_nMemFSysRefCount > 0);
    g_nMemFSysRefCount--;

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

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

/************************************************************************
 *  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 CMemoryFileSystem::InitPlugin(IUnknown* /*IN*/ pContext)
{
    HX_RESULT		lResult;
    IHXPreferences*	prefs = 0;
    IHXBuffer*		base_path_buf = 0;

    if (pContext && !m_pContext)
    {
        m_pContext = pContext;
	m_pContext->AddRef();

	if(!m_options ||
	   (HXR_OK != m_options->GetPropertyBuffer("BasePath", base_path_buf)))
	{
	    lResult = pContext->QueryInterface(IID_IHXPreferences, 
					       (void**) &prefs);
	    if (lResult == HXR_OK)
	    {
		lResult = prefs->ReadPref("BasePath", base_path_buf);
		if (lResult == HXR_OK)
		{
		    m_base_path = CHXString((char*)base_path_buf->GetBuffer());
		}
	    }
	}
	else
	{
	    m_base_path = CHXString((char*)base_path_buf->GetBuffer());
	}
    }

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

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

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *    IHXPlugin::GetPluginInfo
 *  Purpose:
 *    Returns the basic information about this plugin. Including:
 *
 *    unInterfaceCount	the number of standard RMA interfaces 
 *			supported by this plugin DLL.
 *    pIIDList		array of IID's for standard RMA interfaces
 *			supported by this plugin DLL.
 *    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 CMemoryFileSystem::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;

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

    return HXR_OK;
}


// *** IUnknown methods ***

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IUnknown::QueryInterface
//  Purpose:
//	Implement this to export the interfaces supported by your 
//	object.
//
STDMETHODIMP CMemoryFileSystem::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_IHXFileSystemObject))
    {
	AddRef();
	*ppvObj = (IHXFileSystemObject*)this;
	return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXMemoryFileSystem))
    {
	AddRef();
	*ppvObj = (IHXMemoryFileSystem*)this;
	return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXMemoryFileSystem2))
    {
	AddRef();
	*ppvObj = (IHXMemoryFileSystem2*)this;
	return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXValues))
    {
	AddRef();
	*ppvObj = (IHXValues*)this;
	return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

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

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

    delete this;
    return 0;
}

STDMETHODIMP CMemoryFileSystem::GetFileSystemInfo
(
    REF(const char*) /*OUT*/ pShortName,
    REF(const char*) /*OUT*/ pProtocol
)
{
    pShortName	= zm_pShortName;
    pProtocol	= zm_pProtocol;

    return HXR_OK;
}

STDMETHODIMP
CMemoryFileSystem::InitFileSystem(IHXValues* options)
{
    IHXBuffer*		base_path_buf = 0;
    ULONG32         	auth_flag = 0;

    HX_RELEASE(m_options);
    m_options = options;

    if (m_options)
    {
	m_options->AddRef();

	if (HXR_OK == m_options->GetPropertyBuffer("BasePath", base_path_buf))
	{
	    m_base_path = CHXString((char*)base_path_buf->GetBuffer());
	}
    }

    if (base_path_buf)
    {
	base_path_buf->Release();
    }

    return HXR_OK;
}


/////////////////////////////////////////////////////////////////////////
//  Method:
//	IHXFileSystemObject::CreateFile
//  Purpose:
//	TBD
//
STDMETHODIMP CMemoryFileSystem::CreateFile
(
    IUnknown**	/*OUT*/	ppFileObject
)
{
    CMemoryFileObject* pFileObj = 
	new CMemoryFileObject(m_base_path, 
			      this, 
			      m_pContext);

    if (pFileObj)
    {
	if(HXR_OK == pFileObj->QueryInterface(IID_IUnknown,
					    (void**)ppFileObject))
	    return HXR_OK;
	return HXR_UNEXPECTED;
    }
    return HXR_OUTOFMEMORY;
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	CMemoryFileSystem::CreateDir
//  Purpose:
//	TBD
//
STDMETHODIMP CMemoryFileSystem::CreateDir
(
    IUnknown**	/*OUT*/	ppDirObject
)
{
    return HXR_NOTIMPL;
}


/************************************************************************
 *	Method:
 *	    IHXMemoryFileSystem::Add
 *	Purpose:
 *	    Add the specified URL to the file system and create a new
 *	    chunky res for it.
 */
STDMETHODIMP CMemoryFileSystem::Add(const char*		    pURL, 
				    IHXMemoryFileContext*  pContext,
				    void*		    pID,
				    const char*		    pMime)
{
    return AddWithSize(pURL, pContext, pID, pMime, 0);
}


/************************************************************************
 *	Method:
 *	    IHXMemoryFileSystem::AddWithSize
 *	Purpose:
 *	    Add the specified URL to the file system and create a new
 *	    chunky res for it.  Specify the size in advance.
 */
STDMETHODIMP 
CMemoryFileSystem::AddWithSize(const char*		pURL,
			       IHXMemoryFileContext*	pContext,
			       void*			pID,
			       const char*		pMime,
			       UINT32			uSize)
{
    CChunkyRes* pRes = NULL;
    HX_ASSERT(g_pChunkyResMgr);

    CHXURL url(pURL);
    pURL = url.GetURL();

    if (g_pChunkyResMgr->FindResource(pURL) == HXR_OK)
    {
	return HXR_FAIL;
    }

    if (g_pMapStreamToChunky->Lookup(pID, (void*&)pRes))
    {
	return HXR_FAIL;
    }

    // Use the default context if we aren't given a specific one
    if (!pContext)
    {
        HX_ASSERT(z_pMemCtx);
	pContext = z_pMemCtx;
    }

    HX_RESULT theErr = g_pChunkyResMgr->OpenResource(&pRes, pURL);
    if (theErr == HXR_OK)
    {
	CMemoryFileStatus* pStatus = new CMemoryFileStatus(pContext, pID, pMime);
	if (!pStatus)
	{
	    theErr = HXR_OUTOFMEMORY;
	}
	else
	{
	    pStatus->AddRef();
	    pStatus->SetContentSize(uSize);
	    g_pMapChunkyToStatus->SetAt(pRes, pStatus);
	    g_pMapStreamToChunky->SetAt(pID, pRes);
	}
    }

    CancelRequest(pURL, HXR_OK);

    return theErr;
}



/************************************************************************
 *	Method:
 *	    IHXMemoryFileSystem::Remove
 *	Purpose:
 *	    Remove the specified URL from the file system and discard its
 *	    chunky res.
 */
STDMETHODIMP CMemoryFileSystem::Remove(void* pID)
{
    CChunkyRes* pRes = NULL;
    HX_ASSERT(g_pChunkyResMgr);

    if (!g_pMapStreamToChunky->Lookup(pID, (void*&)pRes))
    {
	return HXR_FAIL;
    }

    g_pMapStreamToChunky->RemoveKey(pID);

    HX_ASSERT(pRes);
    CMemoryFileStatus* pStatus = NULL;
    if (pRes && g_pMapChunkyToStatus->Lookup(pRes, (void*&)pStatus))
    {
	// If no one else is using the file, we can close it
	if (!pStatus->Release())
	{
	    g_pMapChunkyToStatus->RemoveKey(pRes);
	    HX_VERIFY(SUCCEEDED(g_pChunkyResMgr->CloseResource(pRes)));
	}
    }

    

    return HXR_OK;
}


/************************************************************************
 *	Method:
 *	    IHXMemoryFileSystem::Exists
 *	Purpose:
 *	    Returns TRUE if the given URL exists in the file system and
 *	    FALSE if it doesn't.
 */
STDMETHODIMP_(BOOL) CMemoryFileSystem::Exists(const char* pURL)
{
    HX_ASSERT(g_pChunkyResMgr);

    CHXURL url(pURL);
    pURL = url.GetURL();

    return g_pChunkyResMgr->FindResource(pURL) == HXR_OK;
}


/************************************************************************
 *	Method:
 *	    IHXMemoryFileSystem::Append
 *	Purpose:
 *	    Appends data to the end of the specified memory file object.
 */
STDMETHODIMP CMemoryFileSystem::Append(void*		pID,
				       unsigned char*	pData,
				       ULONG32		ulLen)
{
    HX_RESULT	theErr	= HXR_FAIL;
    CChunkyRes* pRes	= NULL;

    HX_ASSERT(g_pChunkyResMgr);

    if (g_pMapStreamToChunky->Lookup(pID, (void*&)pRes))
    {
	HX_ASSERT(pRes);
	CMemoryFileStatus* pStatus = NULL;
	if (g_pMapChunkyToStatus->Lookup(pRes, (void*&)pStatus))
	{
	    HX_ASSERT(pStatus);
	    ULONG32 ulPos = pStatus->GetSize();
	    theErr = pRes->SetData(ulPos, (const char*)pData, ulLen);

	    if (theErr == HXR_OK)
	    {
		pStatus->SetSize (ulPos + ulLen);
	    }
	}
    }

    return theErr;
}


/************************************************************************
 *	Method:
 *	    IHXMemoryFileSystem::Finish
 *	Purpose:
 *	    Marks a memory file as having been completely downloaded.
 */
STDMETHODIMP CMemoryFileSystem::Finish(void* pID)
{
    HX_RESULT	theErr	= HXR_FAIL;
    CChunkyRes* pRes	= NULL;

    HX_ASSERT(g_pChunkyResMgr);

    if (g_pMapStreamToChunky->Lookup(pID, (void*&)pRes))
    {
	HX_ASSERT(pRes);
	CMemoryFileStatus* pStatus = NULL;
	if (g_pMapChunkyToStatus->Lookup(pRes, (void*&)pStatus))
	{
	    theErr = HXR_OK;
	    pStatus->SetDone(TRUE);
	}
    }

    return theErr;
}

/************************************************************************
 *	Method:
 *	    IHXMemoryFileSystem::Shutdown
 *	Purpose:
 *	    Informs the memory file system that it is time to 
 *	    release all of its members, if they have not 
 *	    been released yet.
 */
STDMETHODIMP CMemoryFileSystem::Shutdown ()
{
    HX_ASSERT(g_pMapChunkyToStatus);
    CHXMapPtrToPtr::Iterator i;
    for(i = g_pMapChunkyToStatus->Begin();
	i!= g_pMapChunkyToStatus->End(); ++i)
    {
	CMemoryFileStatus* pStatus = (CMemoryFileStatus*)*i;
	HX_ASSERT(pStatus);
	HX_RELEASE(pStatus);
    }
    g_pMapChunkyToStatus->RemoveAll();

    HX_ASSERT(g_pMapStreamToChunky);
    HX_ASSERT(g_pChunkyResMgr);
    for (i = g_pMapStreamToChunky->Begin(); i != g_pMapStreamToChunky->End(); ++i)
    {
	CChunkyRes* pChunk = (CChunkyRes*)*i;
	HX_ASSERT(pChunk);
	HX_VERIFY(SUCCEEDED(g_pChunkyResMgr->CloseResource(pChunk)));
    }
    g_pMapStreamToChunky->RemoveAll();

    return HXR_OK;
}


/************************************************************************
 *	Method:
 *	    IHXMemoryFileSystem::SetDefaultContext
 *	Purpose:
 *	    Provides a default memory file system context for requesting
 *	    new file objects.
 */
STDMETHODIMP
CMemoryFileSystem::SetDefaultContext(IHXMemoryFileContext* pContext)
{
    HX_RELEASE(z_pMemCtx);

    z_pMemCtx = pContext;
    z_pMemCtx->AddRef();

    return HXR_OK;
}


/************************************************************************
 *	Method:
 *	    IHXMemoryFileSystem::RequestURL
 *	Purpose:
 *	    Request that the memory file system ask the default context to
 *	    open the specified URL.
 */
STDMETHODIMP
CMemoryFileSystem::RequestURL(const char* pURL, 
			       IHXFileResponse* pFileResponse)
{
    HX_RESULT theErr = HXR_FAIL;

    CHXURL url(pURL);
    pURL = url.GetURL();

    // don't allow us to have multiple outstanding requests for the
    // same URL
    IHXFileResponse* pOldResponse;
    if (g_pMapURLToResponse->Lookup(pURL, (void*&)pOldResponse))
    {
	return HXR_FAIL;
    }

    if (z_pMemCtx)
    {
	// Mark that we've requested this URL (we mark it before making the
	// request because some file systems are synchronous
	HX_ASSERT(pFileResponse);
	pFileResponse->AddRef();
	g_pMapURLToResponse->SetAt(pURL, pFileResponse);

	theErr = z_pMemCtx->RequestOpen(pURL);

	// the request failed, cancel it internally so we don't wait forever
	if (FAILED(theErr))
	{
	    CancelRequest(pURL, theErr);
	}
    }

    return theErr;
}



/************************************************************************
 *	Method:
 *	    IHXMemoryFileSystem::CancelRequest
 *	Purpose:
 *	    Cancels a pending URL request
 */
STDMETHODIMP
CMemoryFileSystem::CancelRequest(const char* pURL,
				HX_RESULT result)
{
    CHXURL url(pURL);
    pURL = url.GetURL();

    IHXFileResponse* pOldResponse;
    if (g_pMapURLToResponse->Lookup(pURL, (void*&)pOldResponse))
    {
	HX_ASSERT(pOldResponse);
	if (pOldResponse)
	{
	    pOldResponse->InitDone(result);
	}
	HX_RELEASE(pOldResponse);
	g_pMapURLToResponse->RemoveKey(pURL);
    }

    return HXR_OK;
}




CMemoryFileObject::CMemoryFileObject(CHXString& base_path, 
				     IHXFileSystemObject *pFS, 
				     IUnknown* pContext)
    : m_lRefCount(0)
    , m_ulFlags(0)
    , m_bLocalClose(FALSE)
    , m_pContext(pContext)
    , m_pCommonClassFactory(NULL)
    , m_pFileResponse(NULL)
    , m_pChunkyRes(NULL)
    , m_pFileSystem(pFS)
    , m_pFilename(NULL)
    , m_pRequest(0)
    , m_uRecursionCount(0)
    , m_bInReadDone(FALSE)
    , m_pScheduler(NULL)
    , m_pCallback(NULL)
    , m_ulPendingReadCount(0)
    , m_bReadCancelled(FALSE)
    , m_bSeekPending(FALSE)
    , m_ulPos(0)
    , m_bCanBeReOpened(0)
    , m_pStatus(NULL)
    , m_bAsynchInit(FALSE)
    , m_bClosed(FALSE)
{
    g_nMemFSysRefCount++;
    m_base_path = base_path;

    int nLen = m_base_path.GetLength();
    if (nLen > 0)
    {
	if (m_base_path.GetAt(nLen-1) != '/')
	{
	    m_base_path += "/";
	}
    }

    if (m_pFileSystem)
    {
	m_pFileSystem->AddRef();
    }

    HX_ASSERT(m_pContext);
    if (m_pContext)
    {
	m_pContext->AddRef();
	m_pContext->QueryInterface(IID_IHXScheduler, (void**) &m_pScheduler);
	HX_ASSERT(m_pScheduler);
    }

    m_pCallback	= new SMPLFileObjCallback(this);
    HX_ASSERT(m_pCallback);
    m_pCallback->AddRef();
}

CMemoryFileObject::~CMemoryFileObject()
{
    HX_ASSERT(g_nMemFSysRefCount > 0);
    g_nMemFSysRefCount--;
    m_bLocalClose = TRUE;
    Close();
}


// *** IUnknown methods ***

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IUnknown::QueryInterface
//  Purpose:
//	Implement this to export the interfaces supported by your 
//  	object.
//
STDMETHODIMP CMemoryFileObject::QueryInterface(REFIID riid, void** ppvObj)
{
    if (IsEqualIID(riid, IID_IUnknown))
    {
	AddRef();
	*ppvObj = this;
	return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXFileObject))
    {
	AddRef();
	*ppvObj = (IHXFileObject*)this;
	return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXFileStat))
    {
	AddRef();
	*ppvObj = (IHXFileStat*)this;
	return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXFileExists))
    {
	AddRef();
	*ppvObj = (IHXFileExists*)this;
	return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXGetFileFromSamePool))
    {
	AddRef();
	*ppvObj = (IHXGetFileFromSamePool*)this;
	return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXRequestHandler))
    {
	AddRef();
	*ppvObj = (IHXRequestHandler*)this;
	return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXFileMimeMapper))
    {
	AddRef();
	*ppvObj = (IHXFileMimeMapper*)this;
	return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXPendingStatus))
    {
	AddRef();
	*ppvObj = (IHXPendingStatus*)this;
	return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXFileResponse))
    {
	AddRef();
	*ppvObj = (IHXFileResponse*)this;
	return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

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

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

    delete this;
    return 0;
}

/************************************************************************
 *  Method:
 *	IHXFileObject::Init
 *  Purpose:
 *	Associates a file object with the file response object it should
 *	notify of operation completness. This method should also check
 *	for validity of the object (for example by opening it if it is
 *	a local file).
 */
STDMETHODIMP 
CMemoryFileObject::Init
(
    ULONG32		/*IN*/	ulFlags,
    IHXFileResponse*   /*IN*/	pFileResponse
)
{
    HX_RESULT lReturnVal = HXR_OK;
    HX_RESULT resultInitDone = HXR_OK;
    IUnknown* pIUnknownUserContext = NULL;
    IHXRequestContext* pIHXRequestContextCurrent = NULL;
    BOOL bChoseAsynchInit = FALSE;

    if (!pFileResponse) return HXR_INVALID_PARAMETER;
    if (!m_pRequest) return HXR_INVALID_PARAMETER;

    /* Release any previous reponses */
    if (m_pFileResponse)
    {
	m_pFileResponse->Release();
    }

    m_pFileResponse = pFileResponse;
    m_pFileResponse->AddRef();

    /* have we already opened/created a file */
    if (m_pChunkyRes)
    {
	/* if flags are same, then we are all set and there 
	 * is no need to do anything further
	 */
	if (m_ulFlags == ulFlags || ulFlags == 0)
	{
	    m_ulPos = 0;

	    m_pFileResponse->InitDone(HXR_OK);

	    return HXR_OK;
	}

	_CloseFile();
    }
    
    UpdateFileNameMember();
    m_bAsynchInit = FALSE;
    m_ulFlags = ulFlags;

    BOOL bExists = FALSE;
    IHXMemoryFileSystem* pMemFS = GetMemoryFileSystem();
    IHXMemoryFileSystem2* pMemFS2 = NULL;
    if (pMemFS)
    {
	bExists = pMemFS->Exists(m_pFilename);
	if (!bExists)
	{
	    // XXXNH: we have to flag this *before* we request the file, or
	    // else initialization will fail with a synchronous file system
	    m_bAsynchInit = TRUE;
	    bChoseAsynchInit = TRUE;

	    pMemFS2 = GetMemoryFileSystem2();
	    if (pMemFS2)
	    {
		// try to request the file
		lReturnVal = pMemFS2->RequestURL(m_pFilename, this);
	    }
	    else
	    {
		//XXXNH: No IHXMemoryFileSystem2? this should never happen!
		HX_ASSERT(FALSE);
		lReturnVal = HXR_FAIL;
	    }

	    // request failed, so respond with a failure
	    if (lReturnVal != HXR_OK)
	    {
		m_pFileResponse->InitDone(HXR_DOC_MISSING);
		lReturnVal = HXR_DOC_MISSING;

		// XXXNH: we're already initialized: we are missing!
		m_bAsynchInit = FALSE;
		bChoseAsynchInit = FALSE;

		goto exit;
	    }
    
	}
    }
    else
    {
	// XXXNH: No IHXMemoryFileSystem? this should never happen!  
	HX_ASSERT(FALSE);
	m_pFileResponse->InitDone(HXR_FAIL);
	return HXR_FAIL;
    }

    // XXXNH: it is entirely possible that if we're initializing
    // asynchronously that we may have been closed as a result of making the
    // URL request!
    if (!m_bClosed && !bChoseAsynchInit)
    {
	if (!m_pCommonClassFactory)
	{
	    HX_ASSERT(m_pContext);
	    m_pContext->QueryInterface(IID_IHXCommonClassFactory,
				       (void **)&m_pCommonClassFactory);
	}

	if (m_pRequest &&
	    SUCCEEDED(m_pRequest->QueryInterface(
				    IID_IHXRequestContext,
				    (void**)&pIHXRequestContextCurrent)))
	{
	    pIHXRequestContextCurrent->GetUserContext(pIUnknownUserContext);
	    HX_RELEASE(pIHXRequestContextCurrent);
	}

	if (!bChoseAsynchInit)
	{
	    lReturnVal = _OpenFile(ulFlags);
	    HX_RESULT status = m_pChunkyRes ? HXR_OK : HXR_DOC_MISSING;
	    resultInitDone = m_pFileResponse->InitDone(status);
		
	}
    }

exit:
    HX_RELEASE(pIUnknownUserContext);
    HX_RELEASE(pMemFS);
    HX_RELEASE(pMemFS2);

    return (HXR_OK==lReturnVal? resultInitDone:lReturnVal);
}

/************************************************************************
 *  Method:
 *      IHXFileObject::GetFilename
 *  Purpose:
 *      Returns the filename (without any path information) associated
 *      with a file object.
 */
STDMETHODIMP CMemoryFileObject::GetFilename
(
    REF(const char*) /*OUT*/ pFilename
)
{
    UpdateFileNameMember();
    // Find the separator character before the file name
    pFilename = ::strrchr(m_pFilename, '/');

    if (pFilename != NULL) // Found
    {
	// File name starts after the separator charactor
	pFilename++;
    }
    else // Not found
    {
	pFilename = m_pFilename;
    }

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *	IHXFileObject::Close
 *  Purpose:
 *	Closes the file resource and releases all resources associated
 *	with the object.
 */
STDMETHODIMP CMemoryFileObject::Close()
{
    m_bClosed = TRUE;

    if (m_bAsynchInit)
    {
	m_bAsynchInit = FALSE;
	IHXMemoryFileSystem2* pFS = GetMemoryFileSystem2();
	if (pFS)
	{
	    pFS->CancelRequest(m_pFilename, HXR_FAIL);
	}
    }

    // If there is a pending callback, be sure to remove it!
    if (m_pCallback && m_pCallback->m_ulPendingCallbackID && m_pScheduler) 
    {
	m_pScheduler->Remove(m_pCallback->m_ulPendingCallbackID);
    }

    HX_RELEASE(m_pCallback);
    HX_RELEASE(m_pScheduler);

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

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

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

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

    if (m_pChunkyRes) 
    {
	_CloseFile();
    }

    if(m_pFilename)
    {
	delete[] m_pFilename;
	m_pFilename = NULL;
    }
    
    // It is vitally important that the CloseDone be the last step in
    // this method, as the last reference to this object may be
    // released inside CloseDone!  Do not place code after this if block!
    //
    // XXXGH...can't release the m_pFileResponse because it's needed here,
    //         so I've moved the m_pFileResponse->Release() to after the
    //         CloseDone() call and added an AddRef()/Release() pair

    if (!m_bLocalClose)
    {
	AddRef();

	if(m_pFileResponse)
	{
	    m_pFileResponse->CloseDone(HXR_OK);
	    m_pFileResponse->Release();
	    m_pFileResponse = NULL;
        }

	Release();
    }
    else if (m_pFileResponse) 
    {
	m_pFileResponse->Release();
	m_pFileResponse = NULL;
    }

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *	IHXFileObject::Read
 *  Purpose:
 *	Reads a buffer of data of the specified length from the file
 *	and asynchronously returns it to the caller via the 
 *	IHXFileResponse interface passed in to Init.
 */
STDMETHODIMP CMemoryFileObject::Read(ULONG32 ulCount)
{
    HX_ASSERT(m_pFileResponse);

    // XXXBHG, For now, you cant read more than 1MB at a time!
    if (ulCount > 0x000FFFFF)
    {
        HX_ASSERT(!"CMemoryFileObject::Read() - ulCount > 0xFFFFF");
	//Force the system to recognize a failed Read so infinite
	// buffering does not occur with the core waiting for EOF:
	m_pFileResponse->ReadDone(HXR_FAIL, NULL);

	return HXR_INVALID_PARAMETER;
    }

    if (m_bSeekPending)
    {
	m_pFileResponse->ReadDone(HXR_SEEK_PENDING, NULL);
	return HXR_UNEXPECTED;
    }

    if(!m_pChunkyRes && m_bCanBeReOpened)
    {
	_OpenFile(m_ulFlags);
	m_bCanBeReOpened = FALSE;
    }
    if (m_pChunkyRes)
    {
	if(!(m_ulFlags & HX_FILE_READ))
	    return HXR_UNEXPECTED;

	// Do we have enough data?
	ULONG32 ulCurLen = m_pStatus->GetSize();
	if (ulCount + m_ulPos > ulCurLen)
	{
	    // If we're done filling the memory file, read the remainder
	    if (m_pStatus->GetDone())
	    {
		/* Read the last few bytes */
		if (m_ulPos < ulCurLen)
		{
		    ulCount = ulCurLen - m_ulPos;
		}
		else
		{
		    m_bInReadDone = TRUE;

		    // Let the file response sink know about the buffer...
		    HX_RESULT result = HXR_FAIL;
		    m_pFileResponse->ReadDone(result, NULL);
			    
		    m_bInReadDone = FALSE;

		    return result;
		}
	    }
	    else
	    {

		// Request that the context read some more data for us.
		IHXMemoryFileContext* pCtxt = m_pStatus->GetContext();
		if (pCtxt)
		{
		    pCtxt->RequestRead(m_pStatus->GetID(), ulCount);
		    pCtxt->Release();
		}

		// Schedule a callback
		if (m_pScheduler)
		{
		    if (!m_pCallback->m_bCallbackPending)
		    {
			m_pCallback->m_bCallbackPending	= TRUE;
			m_pCallback->m_ulPendingCallbackID  = 
				    m_pScheduler->RelativeEnter(m_pCallback, 0);
		    }
		    //set the count so that the callback will know how much to read.
		    HX_ASSERT(!m_ulPendingReadCount); // XXXSEH: I suspect ulCount should be added to m_ulPendingReadCount.
		    m_ulPendingReadCount = ulCount;

		    return HXR_OK;
		}

		// No scheduler!
		return HXR_FAIL;
	    }
	}

	if (m_bInReadDone)
	{
	    m_uRecursionCount++;
	}

	if (m_uRecursionCount > CMemoryFileSystem::z_uMaxRecursionLevel)
	{
	    //if there already is a callback registered
	    if(m_pCallback->m_bCallbackPending)
	    {
		//see if seek canceled him.  If he did,
		//the file sys called Seek while a Read was
		//pending, so all is good with the callback.
		if(m_bReadCancelled)
		{
		    //seek canceled him, so we will make him valid
		    //and use him.
		    m_pCallback->m_bIgnoreCallback = FALSE;
		    m_bReadCancelled = FALSE;
			//" so IgnoreCallback = FALSE\n");
		}
		else
		{
		    //the fileformat has called us twice and is bad.
		    return HXR_UNEXPECTED;
		}
	    }
	    else
	    { 
		if (m_pScheduler)
		{
		    if (!m_pCallback->m_bCallbackPending)
		    {
			//there was no callback on the schedule, so make a new one.
			m_pCallback->m_bIgnoreCallback = FALSE;
			m_bReadCancelled = FALSE;
			m_pCallback->m_bCallbackPending	= TRUE;
			m_pCallback->m_ulPendingCallbackID  = 
					m_pScheduler->RelativeEnter(m_pCallback, 0);
		    }
		}
		else
		{
		    // no scheduler!
		    return HXR_FAIL;
		}
	    }
	    //set the count so that the callback will know how much to read.
            HX_ASSERT(!m_ulPendingReadCount); // XXXSEH: I suspect ulCount should be added to m_ulPendingReadCount.
	    m_ulPendingReadCount = ulCount;
	    return HXR_OK;
	}

	// Create buffer object here, notice that we call the
	// CreateInstance method of the controller, but we could
	// have implemented our own object that exposed the IHXBuffer
	// interface.
	IHXBuffer* pBuffer;
	HX_RESULT result;

	if (!m_pCommonClassFactory)
	{
	    result = m_pContext->QueryInterface(IID_IHXCommonClassFactory,
	                                        (void **)&m_pCommonClassFactory);

	    if (HXR_OK != result)
	    {
		return result;
	    }
	}

	result = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer,
	                                               (void**)&pBuffer);

	if (HXR_OK != result)
	{
	    return result;
	}

	// Ask the buffer to create a space to read into...
	result = pBuffer->SetSize(ulCount);

	if (HXR_OK != result)
	{
	    return result;
	}

	HX_ASSERT(m_pChunkyRes);

	ULONG32 actual = 0;
	m_pChunkyRes->GetData(m_ulPos, (char*)pBuffer->GetBuffer(), ulCount, &actual);
	HX_ASSERT((actual > 0) && (actual <= ulCount));

	// Tell the buffer about it's real meaningful size
	pBuffer->SetSize(actual);

	m_ulPos += actual;

	m_bInReadDone = TRUE;

	// Let the file response sink know about the buffer...
	result = m_pFileResponse->ReadDone(actual > 0 ? HXR_OK : HXR_FAILED,
	                                   pBuffer);
		
	m_bInReadDone = FALSE;

	if (m_uRecursionCount > 0)
	{
	    m_uRecursionCount--;
	}

	// Release our reference on the buffer!
	pBuffer->Release();

	return result;
    }
    return HXR_UNEXPECTED;
}

/************************************************************************
 *  Method:
 *	IHXFileObject::Write
 *  Purpose:
 *	Not allowed to write to a memory file with this interface
 */
STDMETHODIMP CMemoryFileObject::Write(IHXBuffer* pBuffer)
{
    return HXR_UNEXPECTED;
}

/************************************************************************
 *  Method:
 *	IHXFileObject::Seek
 *  Purpose:
 *	Seeks to an offset in the file and asynchronously notifies
 *	the caller via the IHXFileResponse interface passed in to Init,
 *	of the completeness of the operation.
 */
STDMETHODIMP CMemoryFileObject::Seek(ULONG32 ulOffset, BOOL bRelative)
{
    if (m_bSeekPending)
    {
	m_bSeekPending = FALSE;
	m_pFileResponse->SeekDone(HXR_CANCELLED);
    }

    if(!m_pChunkyRes && m_bCanBeReOpened)
    {
	_OpenFile(m_ulFlags);
    }
    if (m_pChunkyRes)
    {
	/*
	 *  Seek cancels pending Reads.
	 */
	if(m_pCallback && m_pCallback->m_bCallbackPending && !m_bReadCancelled)
	{
	    m_pCallback->m_bIgnoreCallback = TRUE;
	    m_bReadCancelled = TRUE;
	    m_pFileResponse->ReadDone(HXR_CANCELLED, NULL);
	}

	if(bRelative)
	{
	    m_ulPos += ulOffset;
	}
	else
	{
	    m_ulPos = ulOffset;
	}

	if (m_ulPos < m_pStatus->GetSize())
	{
	    return m_pFileResponse->SeekDone(HXR_OK);
	}
	else
	{
	    // XXXNH: until we have pending seek support, respond with an error
	    return m_pFileResponse->SeekDone(HXR_WOULD_BLOCK);
	}
    }
    return HXR_UNEXPECTED;
}


/************************************************************************
 * Method:
 *	IHXFileObject::Stat
 * Purpose:
 *	Collects information about the file that is returned to the
 *	caller in an IHXStat object
 */
STDMETHODIMP CMemoryFileObject::Stat(IHXFileStatResponse* pFileStatResponse)
{
    HX_ASSERT(m_pStatus);
    HX_ASSERT(pFileStatResponse);

    if (m_pStatus)
    {
	pFileStatResponse->StatDone(HXR_OK,
				    m_pStatus->GetContentSize(),
				    0,
				    0,
				    0,
				    0);
    }
    else
    {
	pFileStatResponse->StatDone(HXR_UNEXPECTED, 0, 0, 0, 0, 0);
    }

    return HXR_OK;
}

/************************************************************************
 *  Method:
 *	IHXFileObject::Advise
 *  Purpose:
 *	To pass information to the File Object
 */
STDMETHODIMP CMemoryFileObject::Advise(ULONG32 ulInfo)
{
    HX_RESULT pnr = HXR_OK;

    // If we're done, then we're cached and can allow random access, but
    // otherwise we need to behave like a linear file system
    if ( ( !m_pStatus || !m_pStatus->GetDone() ) && ( ulInfo == HX_FILEADVISE_RANDOMACCESS ) )
    {
        pnr = HXR_ADVISE_PREFER_LINEAR;
    }

    return pnr;
}

/************************************************************************
 *	Method:
 *	    IHXFileObject::GetFileObjectFromPool
 *	Purpose:
 *      To get another FileObject from the same pool. 
 */
STDMETHODIMP CMemoryFileObject::GetFileObjectFromPool (
    IHXGetFileFromSamePoolResponse* response
)
{
    HX_RESULT lReturnVal = HXR_FAILED;
    CMemoryFileObject* pFileObject = 0;
    CHXString new_path;
    CHXString strFileName;
    CHXString strURL;
    IUnknown* pUnknown = 0;

    char* pNewPath    = 0;
    char* pSeparator  = 0;

    UpdateFileNameMember();
    if(!m_pFilename)
    {
	pNewPath = new char[strlen(m_base_path) + 1];
	strcpy(pNewPath, m_base_path); /* Flawfinder: ignore */
    }
    else
    {
	strURL = m_pFilename;
	
	// Make a nice local file name from the URL!
	strFileName = strURL;

	pNewPath = new char[strlen(strFileName) + 1];
	strcpy(pNewPath, (const char*)strFileName); /* Flawfinder: ignore */

	pSeparator = ::strrchr(pNewPath, '/');
	if(pSeparator)
	{
	    /* Separator will be added in seturl */
	    *pSeparator = 0;
	}
	else
	{
	    // started w/filename. no separator implies no path
	    pNewPath[0] = NULL;
	}
    }
    new_path = pNewPath;
    if (pNewPath)
    {
	delete [] pNewPath;
	pNewPath = 0;
    }

    pFileObject = new CMemoryFileObject(new_path,
					m_pFileSystem,
					m_pContext);

    if (!pFileObject)
    {
	return HXR_OUTOFMEMORY;
    }

    lReturnVal = pFileObject->QueryInterface(IID_IUnknown,
					     (void**)&pUnknown);
    
    response->FileObjectReady(lReturnVal == HXR_OK ? 
			      HXR_OK : HXR_FAILED,
			      pUnknown);
    if(pUnknown)
    {
	pUnknown->Release();
	pUnknown = NULL;
    }

    return lReturnVal;

}

// IHXFileExists interface
/************************************************************************
 *	Method:
 *	    IHXFileExists::DoesExist
 *	Purpose:
 */
STDMETHODIMP CMemoryFileObject::DoesExist(const char* /*IN*/  pPath, 
				IHXFileExistsResponse* /*IN*/  pFileResponse)
{
    HX_ASSERT(pFileResponse);
    BOOL bExists = FALSE;

    IHXMemoryFileSystem* pFS = GetMemoryFileSystem();
    if (pFS)
    {
	bExists = pFS->Exists(m_pFilename);
    }

    pFileResponse->DoesExistDone(bExists);
    return HXR_OK;
}

/************************************************************************
 *	Method:
 *	    Private interface::_OpenFile
 *	Purpose:
 *	    open a chunky res
 */
STDMETHODIMP CMemoryFileObject::_OpenFile(ULONG32 ulFlags)
{
    HX_RESULT lReturnVal = HXR_FAIL;
    CHXString strURL;

    UpdateFileNameMember();
    strURL = m_pFilename;

    HX_ASSERT(g_pChunkyResMgr && g_pMapChunkyToStatus);
    HX_ASSERT(!m_pChunkyRes);

    IHXMemoryFileSystem* pFS = GetMemoryFileSystem();
    if (pFS && pFS->Exists(m_pFilename))
    {
	m_ulFlags = ulFlags;
	lReturnVal = g_pChunkyResMgr->OpenResource(&m_pChunkyRes, m_pFilename);

	HX_ASSERT(m_pChunkyRes && lReturnVal == HXR_OK);

	HX_ASSERT(!m_pStatus); // m_pStatus = NULL;
	g_pMapChunkyToStatus->Lookup(m_pChunkyRes, (void*&)m_pStatus);
	HX_ASSERT(m_pStatus);
	m_pStatus->AddRef();
    }

    HX_RELEASE(pFS);

    return lReturnVal;
}

/************************************************************************
 *	Method:
 *	    Private interface::_CloseFile
 *	Purpose:
 *	    close a chunky res
 */
STDMETHODIMP CMemoryFileObject::_CloseFile()
{
    if (m_pChunkyRes)
    {
	if (!m_pStatus->Release())
	{
	    g_pMapChunkyToStatus->RemoveKey(m_pChunkyRes);
	    m_pStatus = NULL;
	    HX_VERIFY(SUCCEEDED(g_pChunkyResMgr->CloseResource(m_pChunkyRes)));
	}
        m_pStatus = NULL;
	m_pChunkyRes = NULL;
    }

    return HXR_OK;
}



void
CMemoryFileObject::UpdateFileNameMember()
{
    if (m_pRequest)
    {
	const char* pURL = 0;

	if (m_pRequest->GetURL(pURL) != HXR_OK)
	{
	    delete[] m_pFilename;

	    m_pFilename = 0;
	}

	if (pURL)
	{
	    CHXString strFilename = m_base_path + pURL;

	    CHXURL url(strFilename);
	    strFilename = url.GetURL();

	    /*
	     * Strip off the parameters
	     */

/*	    INT32 index = strFilename.Find('?');

	    if (index >= 0)
	    {
		strFilename = strFilename.Left(index);
	    }*/

	    /*
	     * Strip off the '+'
	     */

/*	    index = strFilename.Find('+');

	    if (index >= 0)
	    {
		strFilename = strFilename.Left(index);
	    }*/

	    if (m_pFilename)
	    {
		delete[] m_pFilename;
	    }

	    m_pFilename = new_string((const char*)strFilename);
	}
    }
}


STDMETHODIMP CMemoryFileObject::SetRequest
(
    IHXRequest* pRequest
)
{
    if (!pRequest)
    {
	return HXR_INVALID_PARAMETER;
    }

    HX_RELEASE(m_pRequest);

    m_pRequest = pRequest;
    m_pRequest->AddRef();

    UpdateFileNameMember();

    return HXR_OK;
}

STDMETHODIMP CMemoryFileObject::GetRequest
(
    REF(IHXRequest*) pRequest
)
{
    pRequest = m_pRequest;

    if (pRequest)
    {
	pRequest->AddRef();
    }

    return pRequest ? HXR_OK : HXR_FAIL;
}

void
CMemoryFileObject::Process(void)
{
    if (m_ulPendingReadCount > 0)
    {
	UINT32 ulCount = m_ulPendingReadCount;
	m_ulPendingReadCount = 0;
	Read(ulCount);
    }
}


// CMemoryFileObject::SMPLFileObjCallback

CMemoryFileObject::SMPLFileObjCallback::SMPLFileObjCallback(CMemoryFileObject* pFileObject) 
    : m_lRefCount (0)
    , m_pSMPLFileObject (pFileObject)
    , m_bCallbackPending (FALSE)
    , m_ulPendingCallbackID (0)
    , m_bIgnoreCallback(FALSE)
{
    HX_ASSERT(m_pSMPLFileObject);
    g_nMemFSysRefCount++;
}

CMemoryFileObject::SMPLFileObjCallback::~SMPLFileObjCallback()
{
    HX_ASSERT(g_nMemFSysRefCount > 0);
    g_nMemFSysRefCount--;
}

/*
 * IUnknown methods
 */

/////////////////////////////////////////////////////////////////////////
//	Method:
//		IUnknown::QueryInterface
//	Purpose:
//		Implement this to export the interfaces supported by your 
//		object.
//
STDMETHODIMP CMemoryFileObject::SMPLFileObjCallback::QueryInterface(REFIID riid, void** ppvObj)
{
    if (IsEqualIID(riid, IID_IUnknown))
    {
	AddRef();
	*ppvObj = this;
	return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXCallback))
    {
	AddRef();
	*ppvObj = (IHXCallback*)this;
	return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

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

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

    delete this;
    return 0;
}


/*
 *	IHXCallback methods
 */
STDMETHODIMP CMemoryFileObject::SMPLFileObjCallback::Func(void)
{
    /*
     * To ensure the CMemoryFileObject does not get destroyed before we are
     * done with it, we AddRef()/Release() it here
     */

    if (m_pSMPLFileObject)
    {
        HX_ASSERT(m_bCallbackPending);
	m_bCallbackPending	= FALSE;
	m_ulPendingCallbackID	= 0;
	//if seek has not cancelled this...
	if(!m_bIgnoreCallback)
	{
	    m_pSMPLFileObject->AddRef();
	    m_pSMPLFileObject->Process();
	    m_pSMPLFileObject->Release();
	}
	m_bIgnoreCallback = FALSE;
    }

    return HXR_OK;
}



/*
 * IHXPendingStatus methods
 */

/************************************************************************
 *	Method:
 *	    IHXPendingStatus::GetStatus
 *	Purpose:
 *	    Called by the user to get the current pending status from an object
 */
STDMETHODIMP
CMemoryFileObject::GetStatus(REF(UINT16)	uStatusCode,
			     REF(IHXBuffer*)	pStatusDesc,
			     REF(UINT16)	ulPercentDone)
{
    /* Default values*/
    uStatusCode	    = HX_STATUS_READY;
    pStatusDesc	    = 0;
    ulPercentDone   = 0;

    if (!m_pStatus)
    {
	uStatusCode = HX_STATUS_INITIALIZING;
	// we're still initializing!
	return HXR_OK;
    }

    if (m_pStatus->GetDone())
    {
	uStatusCode	= HX_STATUS_READY;
	ulPercentDone	= 0;
    }
    else if (m_bSeekPending || m_ulPendingReadCount)
    {
        uStatusCode = HX_STATUS_BUFFERING;

	ULONG32 ulReadCount = 0;
	if (m_ulPendingReadCount)
	{
	    ulReadCount = (ULONG32)m_ulPendingReadCount;
	}

	int nContentRead = m_pStatus->GetSize();
	if (m_ulPos+ulReadCount)
	{
	    ulPercentDone = (UINT16) ((nContentRead*100)/(m_ulPos+ulReadCount));
	    ulPercentDone = ulPercentDone <= 100 ? ulPercentDone : 100;
	}
	else
	{
	    ulPercentDone = (UINT16) 100;
	}
    }

    return HXR_OK;
}


/************************************************************************
 *	Method:
 *	    GetMemoryFileSystem
 *	Purpose:
 *	    QI the file system for the memory file system interface.
 */
IHXMemoryFileSystem*
CMemoryFileObject::GetMemoryFileSystem(void)
{
    IHXMemoryFileSystem* pFS = NULL;

    if (m_pFileSystem && 
	m_pFileSystem->QueryInterface(IID_IHXMemoryFileSystem, (void**)&pFS) 
	== HXR_OK)
    {
	return pFS;
    }

    return NULL;
}


/************************************************************************
 *	Method:
 *	    GetMemoryFileSystem2
 *	Purpose:
 *	    QI the file system for the memory file system interface.
 */
IHXMemoryFileSystem2*
CMemoryFileObject::GetMemoryFileSystem2(void)
{
    IHXMemoryFileSystem2* pFS = NULL;

    if (m_pFileSystem && 
	m_pFileSystem->QueryInterface(IID_IHXMemoryFileSystem2, (void**)&pFS) 
	== HXR_OK)
    {
	return pFS;
    }

    return NULL;
}


/************************************************************************
 *	Method:
 *	    IHXFileMimeMapper::FindMimeType
 *	Purpose:
 */
STDMETHODIMP
CMemoryFileObject::FindMimeType(const char*		    pURL, 
				IHXFileMimeMapperResponse* pMimeMapperResponse)
{
    HX_ASSERT(pMimeMapperResponse);
    
    IHXValues* pHeader = NULL;
    IHXBuffer* pValue	= NULL;
    HX_RESULT	retVal	= HXR_FAIL;

    if (m_pStatus)
    {
	char* pMime = m_pStatus->GetMime();
	if (pMime)
	{
	    pMimeMapperResponse->MimeTypeFound(HXR_OK, pMime);
	    return HXR_OK;
	}

    }

    // ok if we do not have it, maybe the request object has it.
    
    if (m_pRequest)
    {
	m_pRequest->GetResponseHeaders(pHeader);
	if (pHeader && HXR_OK == pHeader->GetPropertyCString("Content-Type", pValue) && pValue)
	{
	    m_strHeaderContentType = (const char*)pValue->GetBuffer();
	    retVal = HXR_OK;
	    HX_RELEASE(pValue);
	}
	HX_RELEASE(pHeader);
    }

    if (retVal == HXR_OK)
    {
	pMimeMapperResponse->MimeTypeFound(retVal, (const char*)m_strHeaderContentType);
    }
    else
    {
	pMimeMapperResponse->MimeTypeFound(HXR_OK, NULL);
    }
    
    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. If the file is not valid 
 *	    for the file system, the status HXR_FAILED should be 
 *	    returned.
 */
STDMETHODIMP
CMemoryFileObject::InitDone(HX_RESULT status)
{
    if (m_bAsynchInit)
    {
	m_bAsynchInit = FALSE;

	// attempt to init the file
	if (status == HXR_OK)
	{
	    _OpenFile(m_ulFlags);
	}

	if (m_pFileResponse)
	{
	    m_pFileResponse->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
CMemoryFileObject::CloseDone(HX_RESULT status)
{
    return HXR_NOTIMPL;
}

/************************************************************************
 *	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
CMemoryFileObject::ReadDone(HX_RESULT status, IHXBuffer* pBuffer)
{
    return HXR_NOTIMPL;
}

/************************************************************************
 *	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
CMemoryFileObject::WriteDone(HX_RESULT status)
{
    return HXR_NOTIMPL;
}

/************************************************************************
 *	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
CMemoryFileObject::SeekDone(HX_RESULT status)
{
    return HXR_NOTIMPL;
}


/*
 *	IHXValues methods
 */

STDMETHODIMP
CMemoryFileSystem::SetPropertyULONG32(const char*      pPropertyName,
				      ULONG32          uPropertyValue)
{
    if (!strcmp(pPropertyName, MEMFS_RECURSION_DEPTH))
    {
	z_uMaxRecursionLevel = uPropertyValue;

	if (z_uMaxRecursionLevel == 0)
	{
	    // use the default
	    z_uMaxRecursionLevel = MAX_RECURSION_LEVEL;
	}

	return HXR_OK;
    }

    return HXR_FAIL;
}

STDMETHODIMP
CMemoryFileSystem::GetPropertyULONG32(const char*      pPropertyName,
				      REF(ULONG32)     uPropertyValue)
{
    uPropertyValue = 0;

    if (!strcmp(pPropertyName, MEMFS_RECURSION_DEPTH))
    {
	uPropertyValue = z_uMaxRecursionLevel;
	return HXR_OK;
    }

    return HXR_FAIL;
}

