/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbStream
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<12/02/2014: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbStream.h"
#include "xbFileTools.h"
#include "xbXmlItf.h"
#include "xbConfig.h"

#pragma warning(push)
#pragma warning(disable: 4311) //troncation de pointeur de 'wchar *' à 'xbNptr'
/////////////////////////////////////////////////////////////////////////////////////////////
// xbStream
/////////////////////////////////////////////////////////////////////////////////////////////
xbStream::xbStream() 
{
	m_bCRLFasLF = true; 
	m_format = xbFormatUndef;
	m_streamTextCP = m_appTextCP = CP_ACP;
	m_textBomType = xbBOM_none;
}

ulong			xbStream::GetStreamTextCP() const
{
	return m_streamTextCP;
}

void			xbStream::SetStreamTextCP(ulong cp)
{
	m_streamTextCP = cp;
}

ulong			xbStream::GetAppTextCP() const
{
	return m_appTextCP;
}

void			xbStream::SetAppTextCP(ulong cp)
{
	m_appTextCP = cp;
}

void			xbStream::ReadTextBOM(xbTextBOMtype *pbBOMtype)
{
	xbTextBOMread(this,this,this,this,&m_textBomType);
	if (pbBOMtype)
		*pbBOMtype = m_textBomType;
}

void			xbStream::WriteTextBOM(xbTextBOMtype BOMtype)
{
	xbTextBOMwrite(this,this,this,m_textBomType = BOMtype);
}

xbTextBOMtype	xbStream::GetTextBOMtype() const
{
	return m_textBomType;
}
void			xbStream::SetTextBOMtype(xbTextBOMtype BOMtype)
{
	m_textBomType = BOMtype;
}

bool xbStream::GetFileTime(FILETIME *pCreationTime, FILETIME *pLastAccessTime, FILETIME *pLastWriteTime, xbOpMode opm)
{
	if (opm == xbOp_Required)
		xbThrow xbEstream(this,E_NOTIMPL,"GetFileTime() not implemented");

	return false;
}

bool xbStream::SetFileTime(const FILETIME *pCreationTime, const FILETIME *pLastAccessTime, const FILETIME *pLastWriteTime, xbOpMode opm)
{
	if (opm == xbOp_Required)
		xbThrow xbEstream(this,E_NOTIMPL,"SetFileTime() not implemented");

	return false;
}

bool xbStream::GetFileTime(xbDateTime *pCreationTime, xbDateTime *pLastAccessTime, xbDateTime *pLastWriteTime, xbOpMode opm)
{
	FILETIME cti,ati,wti;
	FILETIME	*pcti = pCreationTime?&cti:NULL,
				*pati = pLastAccessTime?&ati:NULL,
				*pwti = pLastWriteTime?&wti:NULL;

	if (!GetFileTime(pcti,pati,pwti,opm))
		return false;
	if (pcti)
		*pCreationTime = *pcti;
	if (pati)
		*pLastAccessTime = *pati;
	if (pwti)
		*pLastWriteTime = *pwti;
	return true;
}

bool xbStream::SetFileTime(const xbDateTime *pCreationTime, const xbDateTime *pLastAccessTime, 
						   const xbDateTime *pLastWriteTime, xbOpMode opm)
{
	FILETIME cti,ati,wti;
	FILETIME	*pcti = NULL,
				*pati = NULL,
				*pwti = NULL;
	if (pCreationTime)
		pCreationTime->ToFileTime(pcti = &cti); 
	if (pLastAccessTime)
		pLastAccessTime->ToFileTime(pati = &ati); 
	if (pLastWriteTime)
		pLastWriteTime->ToFileTime(pwti = &wti); 


	return SetFileTime(pcti,pati,pwti,opm);
}

bool	xbStream::Clone(xbAccessMode om,xbRefPtr<IxbStream> *pprStm, xbOpMode opm)
{
	if (opm == xbOp_Required)
		xbThrow xbEstream(this,E_NOTIMPL,"Clone() not implemented");

	return false;
}

bool	xbStream::Clone(xbRefPtr<IxbStream> *pprStm, xbOpMode opm)
{
	if (opm == xbOp_Required)
		xbThrow xbEstream(this,E_NOTIMPL,"Clone() not implemented");

	return false;
}

IxbInputStream	&xbStream::operator>>(sIxbStreamable *piObj)
{
	piObj->ReadFromStream(this,this);
	return *this;
}

IxbInputStream	&xbStream::operator>>(xbStringA *pStr)
{
	xbStream_ReadStringA(xbFormatBin,this,this,this,this,pStr,NULL);
	return *this;
}

IxbInputStream	&xbStream::operator>>(xbStringW *pStr)
{
	xbStream_ReadStringW(xbFormatBin,this,this,this,this,pStr,NULL);
	return *this;
}

IxbInputStream	&xbStream::operator>>(xbBSTR *pStr)
{
	xbStream_ReadStringW(xbFormatBin,this,this,this,this,pStr,NULL);
	return *this;
}

IxbOutputStream	&xbStream::operator<<(const sIxbStreamable &iObj)
{
	iObj.WriteToStream(this,this);
	return *this;
}

IxbOutputStream	&xbStream::operator<<(const xbStringA &str)
{
	xbStream_WriteStringA(xbFormatBin,this,this,this,str,str.Length,false);
	return *this;
}

IxbOutputStream	&xbStream::operator<<(const xbStringW &str)
{
	xbStream_WriteStringW(xbFormatBin,this,this,this,str,str.Length,false);
	return *this;
}

IxbOutputStream	&xbStream::operator<<(const xbBSTR &str)
{
	xbStream_WriteStringW(xbFormatBin,this,this,this,str,str.Length,false);
	return *this;
}

IxbOutputStream	&xbStream::operator<<(const char *pStr)
{
	xbStream_WriteStringA(xbFormatBin,this,this,this,pStr,(ulong)(pStr?strlen(pStr):0),false);
	return *this;
}

IxbOutputStream	&xbStream::operator<<(const wchar *pStr)
{
	xbStream_WriteStringW(xbFormatBin,this,this,this,pStr,(ulong)(pStr?wcslen(pStr):0),false);
	return *this;
}

ulong	xbStream::ReadStringA(xbStringA *pStr,xbString *pStopStr,IxbStreamMode *pMode)
{
	if (!pMode) 
		pMode = this;
	return xbStream_ReadStringA(xbFormatText,this,pMode,this,this,pStr,pStopStr);
}

ulong	xbStream::ReadStringW(xbStringW *pStr,xbStringW *pStopStr,IxbStreamMode *pMode)
{
	if (!pMode) 
		pMode = this;
	return xbStream_ReadStringW(xbFormatText,this,pMode,this,this,pStr,pStopStr);
}

ulong	xbStream::ReadStringW(xbBSTR *pStr,xbBSTR *pStopStr,IxbStreamMode *pMode)
{
	if (!pMode) 
		pMode = this;
	return xbStream_ReadStringW(xbFormatText,this,pMode,this,this,pStr,pStopStr);
}

ulong	xbStream::WriteStringA(const char *pStr,ulong lgStr,bool bAddLf,IxbStreamMode *pMode)
{
	if (!pMode) 
		pMode = this;
	return xbStream_WriteStringA(xbFormatText,this,pMode,this,pStr,lgStr,bAddLf);
}

ulong	xbStream::WriteStringW(const wchar *pStr,ulong lgStr,bool bAddLf,IxbStreamMode *pMode)
{
	if (!pMode) 
		pMode = this;
	return xbStream_WriteStringW(xbFormatText,this,pMode,this,pStr,lgStr,bAddLf);
}
ulong			xbStream::WriteStringA(const xbStringA &str,bool bAddLf,IxbStreamMode *pMode)
{
	if (!pMode) 
		pMode = this;
	return xbStream_WriteStringA(xbFormatText,this,pMode,this,str,str.Length,bAddLf);
}
ulong			xbStream::WriteStringW(const xbStringW &str,bool bAddLf,IxbStreamMode *pMode)
{
	if (!pMode) 
		pMode = this;
	return xbStream_WriteStringW(xbFormatText,this,pMode,this,str,str.Length,bAddLf);
}
ulong			xbStream::WriteStringW(const xbBSTR &str,bool bAddLf,IxbStreamMode *pMode)
{
	if (!pMode) 
		pMode = this;
	return xbStream_WriteStringW(xbFormatText,this,pMode,this,str,str.Length,bAddLf);
}

//IxbStreamMode methods :
xbFormatMode	xbStream::GetFormat() const
{
	xbLock lck(this);
	return m_format;
}

bool			xbStream::GetCRLFasLF() const
{
	xbLock lck(this);
	return m_bCRLFasLF;
}

void			xbStream::SetFormat(xbFormatMode mode)
{
	xbLock lck(this);
	m_format = mode;
}

void			xbStream::SetCRLFasLF(bool bFlg)
{
	xbLock lck(this);
	m_bCRLFasLF = bFlg;
}

ulong			xbStream::FindLineSepA(ulong startpos,const char *pStr,ulong StrLg,long *pSepLg) const
{
	return xbStream_FindLineSepA(startpos,pStr,StrLg,pSepLg);
}

ulong			xbStream::FindLineSepW(ulong startpos,const wchar *pStr,ulong StrLg,long *pSepLg) const
{
	return xbStream_FindLineSepW(startpos,pStr,StrLg,pSepLg);
}

//ISequentialStream methods :
/* [local] */ HRESULT STDMETHODCALLTYPE xbStream::Read( 
    /* [length_is][size_is][out] */ void __RPC_FAR *pv,
    /* [in] */ ULONG cb,
    /* [out] */ ULONG __RPC_FAR *pcbRead)
{
	eToHRbegin();
	
	ulong ret = Read(pv,cb,pcbRead?false:true);
	if (pcbRead) *pcbRead = ret;
	
	eToHRend();

	return S_OK;
}

/* [local] */ HRESULT STDMETHODCALLTYPE xbStream::Write( 
    /* [size_is][in] */ const void __RPC_FAR *pv,
    /* [in] */ ULONG cb,
    /* [out] */ ULONG __RPC_FAR *pcbWritten)
{
	eToHRbegin();
	
	ulong ret = Write(pv,cb,pcbWritten?false:true);
	if (pcbWritten) *pcbWritten = ret;

	eToHRend();
	
	return S_OK;
}

//Méthodes de IStream.
/* [local] */ HRESULT STDMETHODCALLTYPE xbStream::Seek( 
    /* [in] */ LARGE_INTEGER dlibMove,
    /* [in] */ DWORD dwOrigin,
    /* [out] */ ULARGE_INTEGER __RPC_FAR *plibNewPosition)
{
	eToHRbegin();

	xbSeekMode sm;
	switch(dwOrigin)
	{
	case STREAM_SEEK_SET : sm = xbSeekSet; break;
	case STREAM_SEEK_CUR : sm = xbSeekCur; break;
	case STREAM_SEEK_END : sm = xbSeekEnd; break;
	default:
		xbDbgOutput("xbStream::Seek() : Invalid seek mode !");
		return E_INVALIDARG;
	};

	if (dlibMove.QuadPart < -(int64)2147483648 || dlibMove.QuadPart > 2147483647)
	{
		IxbLongSeekStream *pLongSk;
		if (!xbCastIn<IxbLongSeekStream>(this,&pLongSk))
			return MAKE_HRESULT(SEVERITY_ERROR,FACILITY_STORAGE,ERROR_NOT_SUPPORTED);
		
		uint64 ret = pLongSk->LongSeek(dlibMove.QuadPart,sm);

		if (plibNewPosition) plibNewPosition->QuadPart = ret;

		return S_OK;
	}

	ulong ret = Seek((long)dlibMove.LowPart,sm);
	
	if (plibNewPosition) plibNewPosition->QuadPart = ret;

	eToHRend();
	
	return S_OK;
}

HRESULT STDMETHODCALLTYPE xbStream::SetSize( 
    /* [in] */ ULARGE_INTEGER libNewSize)
{
	eToHRbegin();
	
	if (libNewSize.QuadPart < -(int64)2147483648 || libNewSize.QuadPart > 2147483647)
	{
		IxbLongSeekStream *pLongSk;
		if (!xbCastIn<IxbLongSeekStream>(this,&pLongSk))
			return MAKE_HRESULT(SEVERITY_ERROR,FACILITY_STORAGE,ERROR_NOT_SUPPORTED);
		
		pLongSk->SetLongLength(libNewSize.QuadPart);

		return S_OK;
	}

	SetLength(libNewSize.LowPart);
	
	eToHRend();
	
	return S_OK;
}

HRESULT STDMETHODCALLTYPE xbStream::CopyTo( 
    /* [unique][in] */ IStream __RPC_FAR *pstm,
    /* [in] */ ULARGE_INTEGER cb,
    /* [out] */ ULARGE_INTEGER __RPC_FAR *pcbRead,
    /* [out] */ ULARGE_INTEGER __RPC_FAR *pcbWritten)
{
	HRESULT	hr;

	eToHRbegin();
	
	char	buf[xbSTREAM_BUFSIZE];
	ulong	lg,lg2;
	uint64	cnt=cb.QuadPart;

	if (pcbRead) pcbRead->QuadPart = 0;
	if (pcbWritten) pcbWritten->QuadPart = 0;
	while(cnt > 0)
	{
		if (cnt > sizeof(buf)) lg2 = sizeof(buf);
		else lg2 = (ulong)cnt;
		if ((hr = Read(buf,lg2,&lg)) != S_OK || lg == 0) break;
		if (pcbRead) pcbRead->QuadPart += lg;
		cnt -= lg;
		if ((hr = pstm->Write(buf,lg,&lg2)) != S_OK) break;
		if (pcbWritten) pcbWritten->QuadPart += lg2;
	}

	eToHRend();
	
	return hr;
}

HRESULT STDMETHODCALLTYPE xbStream::Commit( 
    /* [in] */ DWORD grfCommitFlags)
{
	return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE xbStream::Revert( void)
{
	return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE xbStream::LockRegion( 
    /* [in] */ ULARGE_INTEGER libOffset,
    /* [in] */ ULARGE_INTEGER cb,
    /* [in] */ DWORD dwLockType)
{
	return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE xbStream::UnlockRegion( 
    /* [in] */ ULARGE_INTEGER libOffset,
    /* [in] */ ULARGE_INTEGER cb,
    /* [in] */ DWORD dwLockType)
{
	return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE xbStream::Stat( 
    /* [out] */ STATSTG __RPC_FAR *pstatstg,
    /* [in] */ DWORD grfStatFlag)
{
	eToHRbegin();
	
	pstatstg->pwcsName = NULL;
	memset(&pstatstg->mtime,0,sizeof(pstatstg->mtime));
	memset(&pstatstg->ctime,0,sizeof(pstatstg->ctime));
	memset(&pstatstg->atime,0,sizeof(pstatstg->atime));
	pstatstg->grfMode = 0;
    pstatstg->grfLocksSupported = 0;
	memset(&pstatstg->clsid,0,sizeof(pstatstg->clsid));
    pstatstg->grfStateBits = 0;
	pstatstg->type = STGTY_STREAM;
	pstatstg->reserved = 0;
	
	IxbLongSeekStream *pLongSk;
	if (xbCastIn<IxbLongSeekStream>(this,&pLongSk))
		pstatstg->cbSize.QuadPart = pLongSk->GetLongLength();
	else pstatstg->cbSize.QuadPart = GetLength();

	if (!(grfStatFlag & STATFLAG_NONAME))
	{
		xbComData name(GetPhysicalName());
		pstatstg->pwcsName = name.Detach();
	}
	eToHRend();

	return S_OK;
}

HRESULT STDMETHODCALLTYPE xbStream::Clone( 
    /* [out] */ IStream __RPC_FAR *__RPC_FAR *ppstm)
{
	return E_NOTIMPL;
}

uint64			xbStream::WriteStream(sIxbProgress *pCb,IxbInputStream *pstm)
{
	sIxbLock *pLckStm = NULL;
	xbCastIn(pstm,&pLckStm);
	IxbSeekStream *pSkStm = NULL;
	xbCastIn(pstm,&pSkStm);
	return xbStream_WriteStream(this,pstm,pLckStm,pSkStm,pCb);
}
ulong xbStream::WriteStream(sIxbProgress *pCb,IxbInputStream *pstm,ulong DataLg,bool DataLg_Required)
{
	return xbStream_WriteStream(this,pstm,DataLg,DataLg_Required,pCb);
}

/////////////////////////////////////////////////////////////////////////////////////////////
// xbMemoryStream
/////////////////////////////////////////////////////////////////////////////////////////////
bool	xbMemoryStream::Clone(xbAccessMode om,xbRefPtr<IxbStream> *pprStm, xbOpMode opm)
{
	xbMemoryStream *pObj = xbDnew xbMemoryStream(*this,true,om);
	try 
	{
		pObj->Create();
		*pprStm = pObj;
	}
	catch(...)
	{
		pObj->Release();
		throw;
	}
	pObj->Release();

	return true;
}

bool	xbMemoryStream::Clone(xbRefPtr<IxbStream> *pprStm, xbOpMode opm)
{
	xbMemoryStream *pObj = xbDnew xbMemoryStream(*this,true,m_AccessMode);
	try 
	{
		pObj->Create();
		*pprStm = pObj;
	}
	catch(...)
	{
		pObj->Release();
		throw;
	}
	pObj->Release();

	return true;
}

xbMemoryStream::xbMemoryStream(xbAccessMode am)
{
	m_pBuf = xbDnew StBuffer;
	m_CurPos = 0;
	m_AccessMode = am;
	Create();
}

xbMemoryStream::xbMemoryStream(xbMemoryStream &strm,bool shared,xbAccessMode am)
{
	xbSectLock	lck(&strm.m_pBuf->m_sect);
	m_CurPos = 0;
	if (shared)
	{
		m_pBuf = strm.m_pBuf;
		m_pBuf->m_SharedCnt++;
	}
	else
	{
		m_pBuf = xbDnew StBuffer;
		m_AccessMode = xbAccessW;
		WriteStream(NULL,&strm);
		Seek(0);
	}
	Create();
	m_AccessMode = am;
}

xbMemoryStream::~xbMemoryStream()
{
	eProtectBegin();
	{
		xbSectLock	lck(&m_pBuf->m_sect);
		if (--(m_pBuf->m_SharedCnt) <= 0) 
		{
			lck.Unlock(); 
			delete m_pBuf;
		}
	}
	eProtectEnd();
	eProtect(xbStream::Destroy());
}

void		xbMemoryStream::chgBufSz(long size)
{
	xbSectLock	lck(&m_pBuf->m_sect);
	m_pBuf->m_data.SetLength(size);
}

void		xbMemoryStream::growBufIfNecessary(long relsize)
{
	xbSectLock	lck(&m_pBuf->m_sect);
	if (ulong(m_CurPos) + relsize <= m_pBuf->GetLength()) return;
	chgBufSz(m_CurPos + relsize);
}
void		xbMemoryStream::CopyFrom(const xbData &data)
{
	xbSectLock	lck(&m_pBuf->m_sect);
	m_pBuf->m_data = data;
	if (m_CurPos > data.Length) 
		m_CurPos = data.Length;
}

void		xbMemoryStream::CopyTo(xbData *pData) const
{
	xbSectLock	lck(&m_pBuf->m_sect);
	*pData = m_pBuf->m_data;
}

void		xbMemoryStream::Attach(xbData *pData)
{
	xbSectLock	lck(&m_pBuf->m_sect);
	m_pBuf->m_data.Attach(pData);
	if (m_CurPos > m_pBuf->m_data.Length) 
		m_CurPos = m_pBuf->m_data.Length;
}

void		xbMemoryStream::Detach(xbData *pData)
{
	xbSectLock	lck(&m_pBuf->m_sect);
	pData->Attach(&m_pBuf->m_data);
	m_CurPos = 0;
}

//-------------------------------------------------------------------------------------
//MT lock functions (sIxbLock)
void			xbMemoryStream::Lock() const
{
	m_pBuf->m_sect.Lock();
}

void			xbMemoryStream::Unlock() const
{
	m_pBuf->m_sect.Unlock();
}

//-------------------------------------------------------------------------------------
//Read functions (IxbInputStream)
char			xbMemoryStream::ReadChar()
{
	if (!CanRead()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"ReadChar failed, permission denied reading char");
	xbSectLock	lck(&m_pBuf->m_sect);
	if ((ulong)m_CurPos+sizeof(char) > m_pBuf->GetLength()) xbThrow xbEstream(this,STG_E_READFAULT,"ReadChar failed, EOF reached");
	char	chr = *((const char*)((const void*)m_pBuf->m_data) + m_CurPos);
	m_CurPos+=sizeof(char);
	return chr;
}

WCHAR			xbMemoryStream::ReadCharW()
{
	if (!CanRead()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"ReadCharW failed, permission denied reading char");
	xbSectLock	lck(&m_pBuf->m_sect);
	if ((ulong)m_CurPos+sizeof(WCHAR) > m_pBuf->GetLength()) xbThrow xbEstream(this,STG_E_READFAULT,"ReadCharW failed, EOF reached");
	WCHAR	chr = *((const WCHAR*)((const void*)m_pBuf->m_data) + m_CurPos);
	m_CurPos+=sizeof(WCHAR);
	return chr;
}

ulong			xbMemoryStream::Read(void *pData,ulong DataLg,bool DataLg_Required)
{
	if (!CanRead()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"Read failed, permission denied reading data");
	xbSectLock	lck(&m_pBuf->m_sect);
	if (m_CurPos + DataLg > (ulong)m_pBuf->GetLength()) 
	{
		if (DataLg_Required) xbThrow xbEstream(this,STG_E_READFAULT,"Read failed, EOF reached");
		DataLg = m_pBuf->GetLength() - m_CurPos;
	}
	if (DataLg <= 0) return 0;
	LPBYTE	src = (BYTE*)((const void*)m_pBuf->m_data)+m_CurPos; 
	memcpy(pData,src,DataLg);
	m_CurPos += DataLg;
	return DataLg;
}

//-------------------------------------------------------------------------------------
//Write functions (IxbOutputStream)
void			xbMemoryStream::WriteChar(char chr)
{
	if (!CanWrite()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"WriteChar failed, permission denied writing char");
	xbSectLock	lck(&m_pBuf->m_sect);
	growBufIfNecessary(sizeof(chr));
	*((char*)((const void*)m_pBuf->m_data) + m_CurPos) = chr;
	m_CurPos+=sizeof(char);
}

void			xbMemoryStream::WriteCharW(WCHAR chr)
{
	if (!CanWrite()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"WriteCharW failed, permission denied writing char");
	xbSectLock	lck(&m_pBuf->m_sect);
	growBufIfNecessary(sizeof(chr));
	*((WCHAR*)((const void*)m_pBuf->m_data) + m_CurPos) = chr;
	m_CurPos+=sizeof(WCHAR);
}

ulong			xbMemoryStream::Write(const void *pData,ulong DataLg,bool/* DataLg_Required*/)
{
	if (!CanWrite()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"Write failed, permission denied writing data");
	xbSectLock	lck(&m_pBuf->m_sect);
	growBufIfNecessary(DataLg);
	memcpy((BYTE*)((const void*)m_pBuf->m_data)+m_CurPos,pData,DataLg);
	m_CurPos += DataLg;
	return DataLg;
}

//-------------------------------------------------------------------------------------
//Seek functions (IxbSeekStream)
bool			xbMemoryStream::Eof() const
{
	xbSectLock	lck(&m_pBuf->m_sect);
	return ((ulong)m_CurPos >= m_pBuf->GetLength());
}

bool			xbMemoryStream::Bof() const
{
	xbSectLock	lck(&m_pBuf->m_sect);
	return (m_CurPos == 0);
}

ulong			xbMemoryStream::Seek(long pos,xbSeekMode sk)
{
	xbSectLock	lck(&m_pBuf->m_sect);
	long p;
	switch(sk)
	{
	case xbSeekSet : p = pos;break;
	case xbSeekCur : p = m_CurPos+pos;break;
	case xbSeekEnd : 
		{
			p = m_pBuf->GetLength()+pos;
		}
		break;
	default:
		xbThrow xbEstream(this,STG_E_INVALIDPARAMETER,"Seek failed, xbSeekMode mode invalid");
	}
	if (p < 0) xbThrow xbEstream(this,STG_E_SEEKERROR,"Seek failed, position < 0");
	m_CurPos = p;
	
	return m_CurPos;
}

ulong			xbMemoryStream::SetPosition(ulong pos)
{
	xbSectLock	lck(&m_pBuf->m_sect);

	long p = pos;
	if (p < 0) xbThrow xbEstream(this,STG_E_SEEKERROR,"SetPosition failed, position too big");
	m_CurPos = p;
	
	return m_CurPos;
}

void			xbMemoryStream::SetLength(ulong sz)
{
	if (!CanWrite()) 
		xbThrow xbEstream(this,STG_E_ACCESSDENIED,"SetLength failed, permission denied setting size");
	xbSectLock	lck(&m_pBuf->m_sect);
	if (long(sz)<0) 
		xbThrow xbEstream(this,STG_E_ACCESSDENIED,"SetLength failed, invalid size, too big");
	m_CurPos = (long)sz;
	chgBufSz(sz);
}

void			xbMemoryStream::SetEof()
{
	if (!CanWrite()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"SetEof failed, permission denied setting size");
	xbSectLock	lck(&m_pBuf->m_sect);
	chgBufSz(m_CurPos);
}

/////////////////////////////////////////////////////////////////////////////////////////////
// xbFileStream
/////////////////////////////////////////////////////////////////////////////////////////////
bool	xbFileStream::Clone(xbAccessMode om,xbRefPtr<IxbStream> *pprStm, xbOpMode opm)
{
	if (opm == xbOp_Required)
		xbThrow xbEstream(this,E_NOTIMPL,"Clone() not implemented");

	return false;
}

bool	xbFileStream::Clone(xbRefPtr<IxbStream> *pprStm, xbOpMode opm)
{
	if (opm == xbOp_Required)
		xbThrow xbEstream(this,E_NOTIMPL,"Clone() not implemented");

	return false;
}

xbFileStream::xbFileStream(const xbString &fileName,xbOpenMode om,xbAccessMode am,xbWorkMode wm)
{
	m_bStdHandle = false;
	m_fh = INVALID_HANDLE_VALUE;
	m_AccessMode = xbAccessRW;
	Open(fileName,om,am,wm);
	Create();
}

xbFileStream::xbFileStream(const xbSecurityAttr &attr,const xbString &fileName,xbOpenMode om,xbAccessMode am,xbWorkMode wm)
{
	m_bStdHandle = false;
	m_fh = INVALID_HANDLE_VALUE;
	m_AccessMode = xbAccessRW;
	Open(attr,fileName,om,am,wm);
	Create();
}

xbFileStream::xbFileStream()
{
	m_bStdHandle = false;
	m_fh = INVALID_HANDLE_VALUE;
	m_AccessMode = xbAccessRW;
	Create();
}

xbFileStream::~xbFileStream()
{
	eProtect(Close());
	eProtect(xbStream::Destroy());
}

void xbFileStream::Destroy()
{
	Close();
	xbStream::Destroy();
}

//-------------------------------------------------------------------------------------
//xbFileStream dependants functions
bool xbFileStream::GetFileTime(FILETIME *pCreationTime, FILETIME *pLastAccessTime, FILETIME *pLastWriteTime, xbOpMode opm)
{
	if (::GetFileTime(GetHandle(),pCreationTime,pLastAccessTime,pLastWriteTime))
		return true;

	if (opm == xbOp_Required)
		xbThrow xbEstream(this,xbHR_LAST,"xbFileStream::GetFileTime() failed");

	return false;
}

bool xbFileStream::SetFileTime(const FILETIME *pCreationTime, const FILETIME *pLastAccessTime, const FILETIME *pLastWriteTime, xbOpMode opm)
{
	if (::SetFileTime(GetHandle(),pCreationTime,pLastAccessTime,pLastWriteTime))
		return true;

	if (opm == xbOp_Required)
		xbThrow xbEstream(this,xbHR_LAST,"xbFileStream::SetFileTime() failed");

	return false;
}

void	xbFileStream::Open(const xbString &fileName,xbOpenMode om,xbAccessMode am,xbWorkMode wm)
{
	xbSectLock lck(m_pSectPtr);
	Close();
	m_FileName = fileName;
	Open(NULL,om,am,wm);
}

void	xbFileStream::Open(const xbSecurityAttr &attr,const xbString &fileName,xbOpenMode om,xbAccessMode am,xbWorkMode wm)
{
	xbSectLock lck(m_pSectPtr);
	Close();
	m_FileName = fileName;
	Open(&attr,om,am,wm);
}

void	xbFileStream::Open(const xbSecurityAttr *pAttr,xbOpenMode om,xbAccessMode am,xbWorkMode wm)
{
	if (!pAttr && om != xbOpen)
	{
		xbSecurityAttr secAttr;
		xbFileStream::Open(&secAttr,om,am,wm);
		return;
	}

	xbSectLock lck(m_pSectPtr);
	Close();
	m_AccessMode = am;
	DWORD accessmode = 0, openmode = 0, sharemode = 0;
	switch(om)
	{
	case xbOpen : openmode = OPEN_EXISTING; break;
	case xbOpenCreate : openmode = CREATE_NEW; break;
	case xbOpenAlways : openmode = OPEN_ALWAYS; break;
	case xbOpenCreateAlways : openmode = CREATE_ALWAYS; break;
	default:
		xbThrow xbEstream(this,STG_E_INVALIDPARAMETER,"Open(xbOpenMode om,xbAccessMode am,xbWorkMode wm) failed, xbOpenMode invalid");
	};

	switch(am)
	{
	case xbAccessR : accessmode = GENERIC_READ; break;
	case xbAccessW : accessmode = GENERIC_WRITE; break;
	case xbAccessRW : accessmode = GENERIC_READ|GENERIC_WRITE; break;
	default:
		xbThrow xbEstream(this,STG_E_INVALIDPARAMETER,"Open(xbOpenMode om,xbAccessMode am,xbWorkMode wm) failed, xbAccessMode invalid");
	};
	
	switch(wm)
	{
	case xbWorkShared : sharemode = FILE_SHARE_READ|FILE_SHARE_WRITE; break;
	case xbWorkSharedR : sharemode = FILE_SHARE_READ; break;
	case xbWorkSharedW : sharemode = FILE_SHARE_WRITE; break;
	case xbWorkExclusive : sharemode = 0; break;
	default:
		xbThrow xbEstream(this,STG_E_INVALIDPARAMETER,"Open(xbOpenMode om,xbAccessMode am,xbWorkMode wm) failed, xbWorkMode invalid");
	};

	for(;;)
	{
		if (m_FileName == "STDOUT$")
		{
			m_fh = GetStdHandle(STD_OUTPUT_HANDLE);
			if (m_fh != INVALID_HANDLE_VALUE)
			{
				m_bStdHandle = true;
				return;
			}
			m_FileName = "CONOUT$";
			break;
		}
		if (m_FileName == "STDIN$")
		{
			m_fh = GetStdHandle(STD_INPUT_HANDLE);
			if (m_fh != INVALID_HANDLE_VALUE)
			{
				m_bStdHandle = true;
				return;
			}
			m_FileName = "CONIN$";
			break;
		}
		if (m_FileName == "STDERR$")
		{
			m_fh = GetStdHandle(STD_ERROR_HANDLE);
			if (m_fh != INVALID_HANDLE_VALUE)
			{
				m_bStdHandle = true;
				return;
			}
			m_FileName = "CONERR$";
			break;
		}
		break;
	}

	if (strncmp((LPCSTR)m_FileName,"\\\\?\\",4) != 0 && m_FileName.Length < MAX_PATH - 10)
	{
		m_fh = ::CreateFile(m_FileName,accessmode,sharemode,pAttr?pAttr->Ptr():NULL,
			openmode,onGetFileAttribute(),NULL);
		if (m_fh == INVALID_HANDLE_VALUE)
			xbThrow xbEstream(this,xbHR_LAST,"can't open or create file '"+m_FileName+"'");
		return;
	}

	xbStringW wstr;
	if (m_FileName.StartWith("\\\\?\\",4))
		wstr = xbStringW(m_FileName);
	else
	{
		wstr = L"\\\\?\\";
		wstr.Add(xbStringW(m_FileName));
	}

	m_fh = ::CreateFileW(wstr,accessmode,sharemode,pAttr?pAttr->Ptr():NULL,
		openmode,onGetFileAttribute(),NULL);
	if (m_fh == INVALID_HANDLE_VALUE)
		xbThrow xbEstream(this,xbHR_LAST,"can't open or create file '"+m_FileName+"'(W)");
}

void	xbFileStream::Close()
{
	xbSectLock lck(m_pSectPtr);
	if (m_fh == INVALID_HANDLE_VALUE) return;
	CacheFlushRW(true);
	if (!m_bStdHandle)
		::CloseHandle(m_fh);
	m_fh = INVALID_HANDLE_VALUE;
	m_bStdHandle = false;
}

//-------------------------------------------------------------------------------------
//MT lock functions (sIxbLock)
void			xbFileStream::Lock() const
{
	m_pSectPtr->Lock();
}

void			xbFileStream::Unlock() const
{
	m_pSectPtr->Unlock();
}

//-------------------------------------------------------------------------------------
//Read functions (IxbInputStream)
bool			xbFileStream::Eof() const
{
	xbSectLock lck(m_pSectPtr);
	return (GetLongPosition() >= GetLongLength());
}

bool			xbFileStream::Bof() const
{
	return (GetLongPosition() == 0);
}

/*char			xbFileStream::ReadChar()
{
	xbSectLock lck(m_pSectPtr);
	if (!CanRead()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"ReadChar failed, permission denied reading char");
	char	chr;
	DWORD nbchr;
	if (!::ReadFile(m_fh,&chr,sizeof(chr),&nbchr,NULL))
		xbThrow xbEstream(this,xbHR_LAST,"ReadChar failed, EOF reached");
	if (nbchr != sizeof(chr))
		xbThrow xbEstream(this,STG_E_READFAULT,"ReadChar failed, EOF reached");
	return chr;
}

WCHAR			xbFileStream::ReadCharW()
{
	xbSectLock lck(m_pSectPtr);
	if (!CanRead()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"ReadCharW failed, permission denied reading char");
	WCHAR	chr;
	DWORD nbchr;
	if (!::ReadFile(m_fh,&chr,sizeof(chr),&nbchr,NULL))
		xbThrow xbEstream(this,xbHR_LAST,"ReadCharW failed, EOF reached");
	if (nbchr != sizeof(chr))
		xbThrow xbEstream(this,STG_E_READFAULT,"ReadCharW failed, EOF reached");
	return chr;
}

ulong			xbFileStream::Read(void *pData,ulong DataLg,bool DataLg_Required)
{
	xbSectLock lck(m_pSectPtr);
	if (!CanRead()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"Read failed, permission denied reading data");
	DWORD nbchr;
	if (!::ReadFile(m_fh,pData,DataLg,&nbchr,NULL)) 
		xbThrow xbEstream(this,xbHR_LAST,"Read failed, SDK::ReadFile failed");
	if (DataLg_Required && DataLg != nbchr)
		xbThrow xbEstream(this,STG_E_READFAULT,"Read failed, EOF reached");
	return nbchr;
}
*/
ulong			xbFileStream::noCacheRead(uchar *pData,ulong DataLg)
{
	DWORD nbchr;
	if (!::ReadFile(m_fh,pData,DataLg,&nbchr,NULL)) 
		xbThrow xbEstream(this,xbHR_LAST,"Read failed, SDK::ReadFile failed");
	return nbchr;
}

//-------------------------------------------------------------------------------------
//Write functions (IxbOutputStream)
/*
void			xbFileStream::WriteChar(char chr)
{
	xbSectLock lck(m_pSectPtr);
	if (!CanWrite()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"WriteChar failed, permission denied writing char");
	DWORD nbchr;
	if (!::WriteFile(m_fh,&chr,sizeof(chr),&nbchr,NULL)) 
		xbThrow xbEstream(this,xbHR_LAST,"WriteChar failed, SDK::WriteFile failed");
	if (nbchr != sizeof(chr)) 
		xbThrow xbEstream(this,STG_E_WRITEFAULT,"WriteChar failed, SDK::WriteFile failed");
}

void			xbFileStream::WriteCharW(WCHAR chr)
{
	xbSectLock lck(m_pSectPtr);
	if (!CanWrite()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"WriteChar failed, permission denied writing char");
	DWORD nbchr;
	if (!::WriteFile(m_fh,&chr,sizeof(chr),&nbchr,NULL)) 
		xbThrow xbEstream(this,xbHR_LAST,"WriteChar failed, SDK::WriteFile failed");
	if (nbchr != sizeof(chr)) 
		xbThrow xbEstream(this,STG_E_WRITEFAULT,"WriteChar failed, SDK::WriteFile failed");
}

ulong			xbFileStream::Write(const void *pData,ulong DataLg,bool DataLg_Required)
{
	xbSectLock lck(m_pSectPtr);
	if (!CanWrite()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"Write failed, permission denied writing data");
	DWORD nbchr;
	if (!::WriteFile(m_fh,pData,DataLg,&nbchr,NULL)) 
		xbThrow xbEstream(this,xbHR_LAST,"WriteChar failed, SDK::WriteFile failed");
	if (DataLg_Required && nbchr != DataLg) 
		xbThrow xbEstream(this,STG_E_WRITEFAULT,"WriteChar failed, SDK::WriteFile failed");
	return nbchr;
}
*/
ulong			xbFileStream::noCacheWrite(const uchar *pData,ulong DataLg)
{
	DWORD nbchr;
	if (!::WriteFile(m_fh,pData,DataLg,&nbchr,NULL)) 
		xbThrow xbEstream(this,xbHR_LAST,"WriteChar failed, SDK::WriteFile failed");
	return nbchr;
}
//-------------------------------------------------------------------------------------
//Seek functions (IxbSeekStream, 32 bits seek)
ulong			xbFileStream::Seek(long pos,xbSeekMode sk)
{
	long p;
	switch(sk)
	{
	case xbSeekSet : p = FILE_BEGIN;break;
	case xbSeekCur : p = FILE_CURRENT;break;
	case xbSeekEnd : p = FILE_END;break;
	default:
		xbThrow xbEstream(this,STG_E_INVALIDPARAMETER,"Seek failed, xbSeekMode mode invalid");
	}
	xbSectLock lck(m_pSectPtr);
	CacheFlushRW(sk != xbSeekCur);
	ulong ret;
	if ((ret=::SetFilePointer(m_fh,pos,NULL,p)) == 0xffffffff) xbThrow xbEstream(this,xbHR_LAST,"Seek failed");
	return ret;
}

ulong			xbFileStream::SetPosition(ulong pos)
{
	xbSectLock lck(m_pSectPtr);
	CacheFlushRW(true);
	ulong ret;
	if ((ret=::SetFilePointer(m_fh,pos,NULL,FILE_BEGIN)) == 0xffffffff) xbThrow xbEstream(this,xbHR_LAST,"SetPosition failed");
	return ret;
}

void			xbFileStream::SetLength(ulong sz)
{
	xbSectLock lck(m_pSectPtr);
	if (!CanWrite()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"SetEOF failed, permission denied setting size");
	CacheFlushRW();
	if (::SetFilePointer(m_fh,sz,NULL,FILE_BEGIN) == 0xffffffff) xbThrow xbEstream(this,xbHR_LAST,"SetLength() : Seek failed");
	if (!::SetEndOfFile(m_fh))
		xbThrow xbEstream(this,xbHR_LAST,"SetLength() : SetEndOfFile failed");
}

void			xbFileStream::SetEof()
{
	xbSectLock lck(m_pSectPtr);
	CacheFlushRW();
	if (!CanWrite()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"SetEof failed, permission denied setting size");
	if (!::SetEndOfFile(m_fh))
		xbThrow xbEstream(this,xbHR_LAST,"SetEof() : SetEndOfFile failed");
}

ulong			xbFileStream::GetPosition() const
{
	xbSectLock lck(m_pSectPtr);
	ulong CurPos=SetFilePointer(m_fh,0,NULL,FILE_CURRENT);
	if (CurPos == 0xffffffff) xbThrow xbEstream(this,xbHR_LAST,"GetPosition failed");
	return CurPos+m_cache.getSubPosition();
}

ulong			xbFileStream::GetLength() const
{
	xbSectLock lck(m_pSectPtr);
	((xbFileStream*)this)->CacheFlush();
	ulong cur_size=GetFileSize(m_fh,NULL);
	if (cur_size == 0xffffffff) xbThrow xbEstream(this,xbHR_LAST,"GetLength failed");
	return cur_size;
}

//-------------------------------------------------------------------------------------
//Long Seek functions (IxbLongSeekStream, 64 bits seek)
uint64			xbFileStream::LongSeek(int64 pos,xbSeekMode sk)
{
	long p;
	switch(sk)
	{
	case xbSeekSet : p = FILE_BEGIN;break;
	case xbSeekCur : p = FILE_CURRENT;break;
	case xbSeekEnd : p = FILE_END;break;
	default:
		xbThrow xbEstream(this,STG_E_INVALIDPARAMETER,"LongSeek failed, xbSeekMode mode invalid");
	}

	xbSectLock lck(m_pSectPtr);
	CacheFlushRW(sk != xbSeekCur);
	LARGE_INTEGER Pos;
	Pos.QuadPart = pos;
	
	if ((Pos.LowPart=SetFilePointer(m_fh,Pos.LowPart,&Pos.HighPart,p)) == 0xffffffff
		&& GetLastError() != NO_ERROR) xbThrow xbEstream(this,xbHR_LAST,"LongSeek failed");
	
	return Pos.QuadPart;
}

uint64			xbFileStream::GetLongPosition() const
{
	xbSectLock lck(m_pSectPtr);
	ULARGE_INTEGER CurPos;
	CurPos.HighPart = 0;
	CurPos.LowPart = SetFilePointer(m_fh,0,(long*)&CurPos.HighPart,FILE_CURRENT);
	if (CurPos.LowPart == 0xffffffff && GetLastError() != NO_ERROR)
		xbThrow xbEstream(this,xbHR_LAST,"GetLongPosition failed");
	return CurPos.QuadPart+m_cache.getSubPosition();
}

uint64			xbFileStream::SetLongPosition(uint64 pos)
{
	xbSectLock lck(m_pSectPtr);
	CacheFlushRW(true);

	LARGE_INTEGER Pos;
	Pos.QuadPart = pos;
	
	if ((Pos.LowPart=SetFilePointer(m_fh,Pos.LowPart,&Pos.HighPart,FILE_BEGIN)) == 0xffffffff
		&& GetLastError() != NO_ERROR) xbThrow xbEstream(this,xbHR_LAST,"SetLongPosition failed");
	
	return Pos.QuadPart;
};

uint64			xbFileStream::GetLongLength() const
{
	xbSectLock lck(m_pSectPtr);
	((xbFileStream*)this)->CacheFlush();

	ULARGE_INTEGER CurSz;
	CurSz.LowPart=GetFileSize(m_fh,&CurSz.HighPart);
	if (CurSz.LowPart == 0xffffffff && GetLastError() != NO_ERROR)
		xbThrow xbEstream(this,xbHR_LAST,"GetLongLength failed");
	return CurSz.QuadPart;
}

void			xbFileStream::SetLongLength(uint64 sz)
{
	xbSectLock lck(m_pSectPtr);
	if (!CanWrite()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"SetEOF failed, permission denied setting size");
	CacheFlushRW(true);
	ULARGE_INTEGER CurSz;
	CurSz.QuadPart = sz;
	if (SetFilePointer(m_fh,CurSz.LowPart,(long*)&CurSz.HighPart,FILE_BEGIN) == 0xffffffff
		&& GetLastError() != NO_ERROR) 
		xbThrow xbEstream(this,xbHR_LAST,"SetLongLength() : Seek failed");
	if (!::SetEndOfFile(m_fh))
		xbThrow xbEstream(this,xbHR_LAST,"SetLongLength() : SetEndOfFile failed");
}

/////////////////////////////////////////////////////////////////////////////////////////////
// xbTempStream
/////////////////////////////////////////////////////////////////////////////////////////////
xbTempStream::xbTempStream()
{
	m_bTemp = false;
	m_delFileOpMode = xbOp_Required;
}

xbTempStream::xbTempStream(bool bOpen,xbWorkMode wm,xbAccessMode am,bool bAlways)
{
	m_delFileOpMode = xbOp_Required;
	try
	{
		if (bOpen) Open(wm,am,bAlways);
	}
	catch(...)
	{
		eProtectTrace(Destroy());
		throw;
	}
}

xbTempStream::~xbTempStream()
{
	eProtect(Destroy());
}

void xbTempStream::Destroy()
{
	xbSectLock lck(m_pSectPtr);
	xbFileStream::Destroy();
	if (m_FileName.IsValid())
	{
		xbString filename(m_FileName);
		m_FileName.Empty();
		if (m_bTemp) {::xbDeleteFile(filename,m_delFileOpMode); m_bTemp = false;}
	}
}

//-------------------------------------------------------------------------------------
//xbTempStream dependants functions
void	xbTempStream::OpenCreate(xbWorkMode wm,xbAccessMode am,bool bAlways)
{
	xbSectLock lck(m_pSectPtr);
	Close();
	if (!m_bTemp || m_FileName.IsEmpty())
	{
		m_bTemp = true;
		m_FileName = ::xbGetTempFile();
	}
	xbFileStream::Open(NULL,bAlways?xbOpenCreateAlways:xbOpenCreate,am,wm);
}

void	xbTempStream::Open(xbWorkMode wm,xbAccessMode am,bool bAlways)
{
	xbSectLock lck(m_pSectPtr);
	Close();
	if (!m_bTemp || m_FileName.IsEmpty())
	{
		m_bTemp = true;
		m_FileName = ::xbGetTempFile();
	}
	xbFileStream::Open(NULL,bAlways?xbOpenAlways:xbOpen,am,wm);
}

void	xbTempStream::OpenNew(bool bDelPrevious,xbWorkMode wm,xbAccessMode am,bool bAlways)
{
	xbSectLock lck(m_pSectPtr);
	Close();
	if (m_FileName.IsValid())
	{
		xbString filename(m_FileName);
		m_FileName.Empty();
		if (m_bTemp) 
		{
			if (bDelPrevious) ::xbDeleteFile(filename,m_delFileOpMode); 
			m_bTemp = false;
		}
	}
	if (!m_bTemp || m_FileName.IsEmpty())
	{
		m_bTemp = true;
		m_FileName = ::xbGetTempFile();
	}
	xbFileStream::Open(NULL,bAlways?xbOpenCreateAlways:xbOpenCreate,am,wm);
}

void	xbTempStream::OpenCreate(bool bTemporaryFile,const xbString &file,xbWorkMode wm,xbAccessMode am,bool bAlways)
{
	xbSectLock lck(m_pSectPtr);
	Close();
	if (m_FileName.IsValid())
	{
		xbString filename(m_FileName);
		m_FileName.Empty();
		if (m_bTemp) 
		{
			::xbDeleteFile(filename,m_delFileOpMode); 
			m_bTemp = false;
		}
	}
	m_FileName = file;
	m_bTemp = bTemporaryFile;
	try
	{
		xbFileStream::Open(NULL,bAlways?xbOpenCreateAlways:xbOpenCreate,am,wm);
	}
	catch(...)
	{
		m_FileName.Empty();
		m_bTemp = false;
		throw;
	}
}

void	xbTempStream::Open(bool bTemporaryFile,const xbString &file,xbWorkMode wm,xbAccessMode am,bool bAlways)
{
	xbSectLock lck(m_pSectPtr);
	Close();
	if (m_FileName.IsValid())
	{
		xbString filename(m_FileName);
		m_FileName.Empty();
		if (m_bTemp) 
		{
			::xbDeleteFile(filename,m_delFileOpMode); 
			m_bTemp = false;
		}
	}
	m_FileName = file;
	m_bTemp = bTemporaryFile;
	try
	{
		xbFileStream::Open(NULL,bAlways?xbOpenAlways:xbOpen,am,wm);
	}
	catch(...)
	{
		m_FileName.Empty();
		m_bTemp = false;
		throw;
	}
}

void	xbTempStream::OpenCreate(const xbSecurityAttr &attr,xbWorkMode wm,xbAccessMode am,bool bAlways)
{
	xbSectLock lck(m_pSectPtr);
	Close();
	if (!m_bTemp || m_FileName.IsEmpty())
	{
		m_bTemp = true;
		m_FileName = ::xbGetTempFile();
	}
	xbFileStream::Open(&attr,bAlways?xbOpenCreateAlways:xbOpenCreate,am,wm);
}

void	xbTempStream::Open(const xbSecurityAttr &attr,xbWorkMode wm,xbAccessMode am,bool bAlways)
{
	xbSectLock lck(m_pSectPtr);
	Close();
	if (!m_bTemp || m_FileName.IsEmpty())
	{
		m_bTemp = true;
		m_FileName = ::xbGetTempFile();
	}
	xbFileStream::Open(&attr,bAlways?xbOpenAlways:xbOpen,am,wm);
}

void	xbTempStream::OpenNew(const xbSecurityAttr &attr,bool bDelPrevious,xbWorkMode wm,xbAccessMode am,bool bAlways)
{
	xbSectLock lck(m_pSectPtr);
	Close();
	if (m_FileName.IsValid())
	{
		xbString filename(m_FileName);
		m_FileName.Empty();
		if (m_bTemp) 
		{
			if (bDelPrevious) ::xbDeleteFile(filename,m_delFileOpMode); 
			m_bTemp = false;
		}
	}
	if (!m_bTemp || m_FileName.IsEmpty())
	{
		m_bTemp = true;
		m_FileName = ::xbGetTempFile();
	}
	xbFileStream::Open(&attr,bAlways?xbOpenCreateAlways:xbOpenCreate,am,wm);
}

void	xbTempStream::OpenCreate(const xbSecurityAttr &attr,bool bTemporaryFile,const xbString &file,xbWorkMode wm,xbAccessMode am,bool bAlways)
{
	xbSectLock lck(m_pSectPtr);
	Close();
	if (m_FileName.IsValid())
	{
		xbString filename(m_FileName);
		m_FileName.Empty();
		if (m_bTemp) 
		{
			::xbDeleteFile(filename,m_delFileOpMode); 
			m_bTemp = false;
		}
	}
	m_FileName = file;
	m_bTemp = bTemporaryFile;
	try
	{
		xbFileStream::Open(&attr,bAlways?xbOpenCreateAlways:xbOpenCreate,am,wm);
	}
	catch(...)
	{
		m_FileName.Empty();
		m_bTemp = false;
		throw;
	}
}

void	xbTempStream::Open(const xbSecurityAttr &attr,bool bTemporaryFile,const xbString &file,xbWorkMode wm,xbAccessMode am,bool bAlways)
{
	xbSectLock lck(m_pSectPtr);
	Close();
	if (m_FileName.IsValid())
	{
		xbString filename(m_FileName);
		m_FileName.Empty();
		if (m_bTemp) 
		{
			::xbDeleteFile(filename,m_delFileOpMode); 
			m_bTemp = false;
		}
	}
	m_FileName = file;
	m_bTemp = bTemporaryFile;
	try
	{
		xbFileStream::Open(&attr,bAlways?xbOpenAlways:xbOpen,am,wm);
	}
	catch(...)
	{
		m_FileName.Empty();
		m_bTemp = false;
		throw;
	}
}


/////////////////////////////////////////////////////////////////////////////////////////////
// xbNoLockStream
/////////////////////////////////////////////////////////////////////////////////////////////
IxbInputStream	&xbNoLockStream::operator>>(sIxbStreamable *piObj)
{
	piObj->ReadFromStream(NULL,this);
	return *this;
}

IxbInputStream	&xbNoLockStream::operator>>(xbStringA *pStr)
{
	xbStream_ReadStringA(xbFormatBin,this,NULL,this,NULL,pStr,NULL);
	return *this;
}

IxbInputStream	&xbNoLockStream::operator>>(xbStringW *pStr)
{
	xbStream_ReadStringW(xbFormatBin,this,NULL,this,NULL,pStr,NULL);
	return *this;
}

IxbInputStream	&xbNoLockStream::operator>>(xbBSTR *pStr)
{
	xbStream_ReadStringW(xbFormatBin,this,NULL,this,NULL,pStr,NULL);
	return *this;
}

IxbOutputStream	&xbNoLockStream::operator<<(const sIxbStreamable &iObj)
{
	iObj.WriteToStream(NULL,this);
	return *this;
}

IxbOutputStream	&xbNoLockStream::operator<<(const xbStringA &str)
{
	xbStream_WriteStringA(xbFormatBin,this,NULL,NULL,str,str.Length,false);
	return *this;
}

IxbOutputStream	&xbNoLockStream::operator<<(const xbStringW &str)
{
	xbStream_WriteStringW(xbFormatBin,this,NULL,NULL,str,str.Length,false);
	return *this;
}

IxbOutputStream	&xbNoLockStream::operator<<(const xbBSTR &str)
{
	xbStream_WriteStringW(xbFormatBin,this,NULL,NULL,str,str.Length,false);
	return *this;
}

IxbOutputStream	&xbNoLockStream::operator<<(const char *pStr)
{
	xbStream_WriteStringA(xbFormatBin,this,NULL,NULL,pStr,(ulong)(pStr?strlen(pStr):0),false);
	return *this;
}

IxbOutputStream	&xbNoLockStream::operator<<(const wchar *pStr)
{
	xbStream_WriteStringW(xbFormatBin,this,NULL,NULL,pStr,(ulong)(pStr?wcslen(pStr):0),false);
	return *this;
}

ulong	xbNoLockStream::ReadStringA(xbStringA *pStr,xbString *pStopStr,IxbStreamMode *pMode)
{
	return xbStream_ReadStringA(xbFormatText,this,pMode,this,NULL,pStr,pStopStr);
}

ulong	xbNoLockStream::ReadStringW(xbStringW *pStr,xbStringW *pStopStr,IxbStreamMode *pMode)
{
	return xbStream_ReadStringW(xbFormatText,this,pMode,this,NULL,pStr,pStopStr);
}

ulong	xbNoLockStream::ReadStringW(xbBSTR *pStr,xbBSTR *pStopStr,IxbStreamMode *pMode)
{
	return xbStream_ReadStringW(xbFormatText,this,pMode,this,NULL,pStr,pStopStr);
}

ulong	xbNoLockStream::WriteStringA(const char *pStr,ulong lgStr,bool bAddLf,IxbStreamMode *pMode)
{
	return xbStream_WriteStringA(xbFormatText,this,pMode,NULL,pStr,lgStr,bAddLf);
}

ulong	xbNoLockStream::WriteStringW(const wchar *pStr,ulong lgStr,bool bAddLf,IxbStreamMode *pMode)
{
	return xbStream_WriteStringW(xbFormatText,this,pMode,NULL,pStr,lgStr,bAddLf);
}
ulong			xbNoLockStream::WriteStringA(const xbStringA &str,bool bAddLf,IxbStreamMode *pMode)
{
	return xbStream_WriteStringA(xbFormatText,this,pMode,NULL,str,str.Length,bAddLf);
}
ulong			xbNoLockStream::WriteStringW(const xbStringW &str,bool bAddLf,IxbStreamMode *pMode)
{
	return xbStream_WriteStringW(xbFormatText,this,pMode,NULL,str,str.Length,bAddLf);
}
ulong			xbNoLockStream::WriteStringW(const xbBSTR &str,bool bAddLf,IxbStreamMode *pMode)
{
	return xbStream_WriteStringW(xbFormatText,this,pMode,NULL,str,str.Length,bAddLf);
}


bool xbNoLockStream::GetFileTime(FILETIME *pCreationTime, FILETIME *pLastAccessTime, FILETIME *pLastWriteTime, xbOpMode opm)
{
	if (opm == xbOp_Required)
		xbThrow xbEstream(this,E_NOTIMPL,"xbNoLockStream::GetFileTime() not implemented");

	return false;
}

bool xbNoLockStream::SetFileTime(const FILETIME *pCreationTime, const FILETIME *pLastAccessTime, const FILETIME *pLastWriteTime, xbOpMode opm)
{
	if (opm == xbOp_Required)
		xbThrow xbEstream(this,E_NOTIMPL,"xbNoLockStream::SetFileTime() not implemented");

	return false;
}

bool xbNoLockStream::GetFileTime(xbDateTime *pCreationTime, xbDateTime *pLastAccessTime, xbDateTime *pLastWriteTime, xbOpMode opm)
{
	FILETIME cti,ati,wti;
	FILETIME	*pcti = pCreationTime?&cti:NULL,
				*pati = pLastAccessTime?&ati:NULL,
				*pwti = pLastWriteTime?&wti:NULL;

	if (!GetFileTime(pcti,pati,pwti,opm))
		return false;
	if (pcti)
		*pCreationTime = *pcti;
	if (pati)
		*pLastAccessTime = *pati;
	if (pwti)
		*pLastWriteTime = *pwti;
	return true;
}

bool xbNoLockStream::SetFileTime(const xbDateTime *pCreationTime, const xbDateTime *pLastAccessTime, 
						   const xbDateTime *pLastWriteTime, xbOpMode opm)
{
	FILETIME cti,ati,wti;
	FILETIME	*pcti = NULL,
				*pati = NULL,
				*pwti = NULL;
	if (pCreationTime)
		pCreationTime->ToFileTime(pcti = &cti); 
	if (pLastAccessTime)
		pLastAccessTime->ToFileTime(pati = &ati); 
	if (pLastWriteTime)
		pLastWriteTime->ToFileTime(pwti = &wti); 


	return SetFileTime(pcti,pati,pwti,opm);
}

bool	xbNoLockStream::Clone(xbAccessMode om,xbRefPtr<IxbStream> *pprStm, xbOpMode opm)
{
	if (opm == xbOp_Required)
		xbThrow xbEstream(this,E_NOTIMPL,"Clone() not implemented");

	return false;
}

bool	xbNoLockStream::Clone(xbRefPtr<IxbStream> *pprStm, xbOpMode opm)
{
	if (opm == xbOp_Required)
		xbThrow xbEstream(this,E_NOTIMPL,"Clone() not implemented");

	return false;
}

/*
//IxbStreamMode methods :
xbFormatMode	xbNoLockStream::GetFormat() const
{
	return m_format;
}

bool			xbNoLockStream::GetCRLFasLF() const
{
	return m_bCRLFasLF;
}

void			xbNoLockStream::SetFormat(xbFormatMode mode)
{
	m_format = mode;
}

void			xbNoLockStream::SetCRLFasLF(bool bFlg)
{
	m_bCRLFasLF = bFlg;
}

ulong			xbNoLockStream::FindLineSep(int startpos,const char *pStr,ulong StrLg,ulong *pSepLg) const
{
	return xbStream_FindLineSep(startpos,pStr,StrLg,pSepLg);
}

ulong			xbNoLockStream::FindLineSepW(int startpos,const wchar *pStr,ulong StrLg,ulong *pSepLg) const
{
	return xbStream_FindLineSepW(startpos,pStr,StrLg,pSepLg);
}

*/


uint64			xbNoLockStream::WriteStream(sIxbProgress *pCb,IxbInputStream *pstm)
{
	sIxbLock *pLckStm = NULL;
	xbCastIn(pstm,&pLckStm);
	IxbSeekStream *pSkStm = NULL;
	xbCastIn(pstm,&pSkStm);
	return xbStream_WriteStream(this,pstm,pLckStm,pSkStm,pCb);
}
ulong xbNoLockStream::WriteStream(sIxbProgress *pCb,IxbInputStream *pstm,ulong DataLg,bool DataLg_Required)
{
	return xbStream_WriteStream(this,pstm,DataLg,DataLg_Required,pCb);
}

/////////////////////////////////////////////////////////////////////////////////////////////
// xbDataStream
/////////////////////////////////////////////////////////////////////////////////////////////
xbDataStream::xbDataStream(xbData *pData,xbAccessMode am)
{
	m_pData = pData;
	m_CurPos = 0;
	m_AccessMode = am;
	Create();
}

xbDataStream::~xbDataStream()
{
	m_pData = NULL;
	eProtect(xbNoLockStream::Destroy());
}

void		xbDataStream::chgBufSz(long size)
{
	m_pData->SetLength(size);
}

void		xbDataStream::growBufIfNecessary(long relsize)
{
	if (m_CurPos + relsize <= m_pData->GetLength()) 
		return;
	chgBufSz(m_CurPos + relsize);
}
//-------------------------------------------------------------------------------------
//Read functions (IxbInputStream)
bool			xbDataStream::Eof() const
{
	return (m_CurPos >= m_pData->GetLength());
}

bool			xbDataStream::Bof() const
{
	return (m_CurPos == 0);
}

char			xbDataStream::ReadChar()
{
	if (!CanRead()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"ReadChar failed, permission denied reading char");
	if (m_CurPos+(long)sizeof(char) > m_pData->GetLength()) xbThrow xbEstream(this,STG_E_READFAULT,"ReadChar failed, EOF reached");
	char	chr = *((const char*)((const void*)*m_pData) + m_CurPos);
	m_CurPos+=sizeof(char);
	return chr;
}

WCHAR			xbDataStream::ReadCharW()
{
	if (!CanRead()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"ReadCharW failed, permission denied reading char");
	if (m_CurPos+(long)sizeof(WCHAR) > m_pData->GetLength()) xbThrow xbEstream(this,STG_E_READFAULT,"ReadCharW failed, EOF reached");
	WCHAR	chr = *((const WCHAR*)((const void*)*m_pData) + m_CurPos);
	m_CurPos+=sizeof(WCHAR);
	return chr;
}

ulong			xbDataStream::Read(void *pData,ulong DataLg,bool DataLg_Required)
{
	if (!CanRead()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"Read failed, permission denied reading data");
	if (m_CurPos + DataLg > (ulong)m_pData->GetLength()) 
	{
		if (DataLg_Required) xbThrow xbEstream(this,STG_E_READFAULT,"Read failed, EOF reached");
		DataLg = m_pData->GetLength() - m_CurPos;
	}
	if (DataLg <= 0) return 0;
	LPBYTE	src = (BYTE*)((const void*)*m_pData)+m_CurPos; 
	memcpy(pData,src,DataLg);
	m_CurPos += DataLg;
	return DataLg;
}

//-------------------------------------------------------------------------------------
//Write functions (IxbOutputStream)
void			xbDataStream::WriteChar(char chr)
{
	if (!CanWrite()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"WriteChar failed, permission denied writing char");
	growBufIfNecessary(sizeof(chr));
	*((char*)((const void*)*m_pData) + m_CurPos) = chr;
	m_CurPos+=sizeof(char);
}

void			xbDataStream::WriteCharW(WCHAR chr)
{
	if (!CanWrite()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"WriteCharW failed, permission denied writing char");
	growBufIfNecessary(sizeof(chr));
	*((WCHAR*)((const void*)*m_pData) + m_CurPos) = chr;
	m_CurPos+=sizeof(WCHAR);
}

ulong			xbDataStream::Write(const void *pData,ulong DataLg,bool/* DataLg_Required*/)
{
	if (!CanWrite()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"Write failed, permission denied writing data");
	growBufIfNecessary(DataLg);
	memcpy((BYTE*)((const void*)*m_pData)+m_CurPos,pData,DataLg);
	m_CurPos += DataLg;
	return DataLg;
}

//-------------------------------------------------------------------------------------
//Seek functions (IxbSeekStream)
ulong			xbDataStream::Seek(long pos,xbSeekMode sk)
{
	long p;
	switch(sk)
	{
	case xbSeekSet : p = pos;break;
	case xbSeekCur : p = m_CurPos+pos;break;
	case xbSeekEnd : 
		{
			p = m_pData->GetLength()+pos;
		}
		break;
	default:
		xbThrow xbEstream(this,STG_E_INVALIDPARAMETER,"Seek failed, xbSeekMode mode invalid");
	}
	if (p < 0) xbThrow xbEstream(this,STG_E_SEEKERROR,"Seek failed, position < 0");
	m_CurPos = p;
	
	return m_CurPos;
}

ulong			xbDataStream::SetPosition(ulong pos)
{
	long p = pos;
	if (p < 0) xbThrow xbEstream(this,STG_E_SEEKERROR,"SetPosition failed, position too big");
	m_CurPos = p;
	
	return m_CurPos;
}

void			xbDataStream::SetLength(ulong sz)
{
	if (!CanWrite()) xbThrow xbEstream(this,STG_E_ACCESSDENIED,"SetLength failed, permission denied setting size");
	if (long(sz)<0) 
		xbThrow xbEstream(this,STG_E_ACCESSDENIED,"SetLength failed, invalid size, too big");
	m_CurPos = (long)sz;
	chgBufSz(sz);
}

void			xbDataStream::SetEof()
{
	if (!CanWrite()) 
		xbThrow xbEstream(this,STG_E_ACCESSDENIED,"SetLength failed, permission denied setting size");
	chgBufSz(m_CurPos);
}




/////////////////////////////////////////////////////////////////////////////////////////////
// xbConstStream
/////////////////////////////////////////////////////////////////////////////////////////////
xbConstStream::xbConstStream(const void *pData,int cByte,xbAccessMode om)
{
	m_pData = (const uchar *)pData;
	m_pDataEnd = m_pData + cByte;
	m_pCurPos = (uchar*)m_pData;
	m_AccessMode = om;

	Create();
}

xbConstStream::~xbConstStream()
{
	m_pData = m_pDataEnd = m_pCurPos = NULL;
	eProtect(xbNoLockStream::Destroy());
}

//-------------------------------------------------------------------------------------
//Read functions (IxbInputStream)
bool			xbConstStream::Eof() const
{
	return m_pCurPos >= m_pDataEnd;
}

bool			xbConstStream::Bof() const
{
	return m_pCurPos == m_pData;
}

char			xbConstStream::ReadChar()
{
	if (!CanRead()) 
		xbThrow xbEstream(this,STG_E_ACCESSDENIED,"ReadChar failed, permission denied reading char");
	if (m_pCurPos >= m_pDataEnd) 
		xbThrow xbEstream(this,STG_E_READFAULT,"ReadChar failed, EOF reached");
	char	chr = *((const char*)m_pCurPos);
	m_pCurPos++;
	return chr;
}

WCHAR			xbConstStream::ReadCharW()
{
	if (!CanRead()) 
		xbThrow xbEstream(this,STG_E_ACCESSDENIED,"ReadCharW failed, permission denied reading char");
	if (m_pCurPos + sizeof(WCHAR) > m_pDataEnd) 
		xbThrow xbEstream(this,STG_E_READFAULT,"ReadCharW failed, EOF reached");
	WCHAR	chr = *((const WCHAR*)m_pCurPos);
	m_pCurPos+=sizeof(WCHAR);
	return chr;
}

ulong			xbConstStream::Read(void *pData,ulong DataLg,bool DataLg_Required)
{
	if (!CanRead()) 
		xbThrow xbEstream(this,STG_E_ACCESSDENIED,"Read failed, permission denied reading data");
	if (m_pCurPos + DataLg > m_pDataEnd) 
	{
		if (DataLg_Required) 
			xbThrow xbEstream(this,STG_E_READFAULT,"Read failed, EOF reached");
		DataLg = ulong(xbNptr(m_pDataEnd) - xbNptr(m_pCurPos));
	}
	if (DataLg <= 0) 
		return 0;
	memcpy(pData,m_pCurPos,DataLg);
	m_pCurPos += DataLg;
	return DataLg;
}

//-------------------------------------------------------------------------------------
//Write functions (IxbOutputStream)
void			xbConstStream::WriteChar(char chr)
{
	if (!CanWrite()) 
		xbThrow xbEstream(this,STG_E_ACCESSDENIED,"WriteChar failed, permission denied writing char");
	if (m_pCurPos >= m_pDataEnd) 
		xbThrow xbEstream(this,STG_E_READFAULT,"WriteChar failed, EOF reached");
	*m_pCurPos = chr;
	m_pCurPos++;
}

void			xbConstStream::WriteCharW(WCHAR chr)
{
	if (!CanWrite()) 
		xbThrow xbEstream(this,STG_E_ACCESSDENIED,"WriteCharW failed, permission denied writing char");
	if (m_pCurPos + sizeof(WCHAR) > m_pDataEnd) 
		xbThrow xbEstream(this,STG_E_READFAULT,"WriteChar failed, EOF reached");
	*((WCHAR*)m_pCurPos) = chr;
	m_pCurPos+=sizeof(WCHAR);
}

ulong			xbConstStream::Write(const void *pData,ulong DataLg,bool/* DataLg_Required*/)
{
	if (!CanWrite()) 
		xbThrow xbEstream(this,STG_E_ACCESSDENIED,"Write failed, permission denied writing data");
	if (m_pCurPos + DataLg > m_pDataEnd) 
		xbThrow xbEstream(this,STG_E_READFAULT,"WriteChar failed, EOF reached");
	memcpy(m_pCurPos,pData,DataLg);
	m_pCurPos += DataLg;
	return DataLg;
}

//-------------------------------------------------------------------------------------
//Seek functions (IxbSeekStream)
ulong			xbConstStream::Seek(long pos,xbSeekMode sk)
{
	long p;
	switch(sk)
	{
	case xbSeekSet : p = pos; break;
	case xbSeekCur : p = long(xbNptr(m_pCurPos) - xbNptr(m_pData)) + pos; break;
	case xbSeekEnd : p = long(xbNptr(m_pDataEnd) - xbNptr(m_pData))+pos; break;
	default:
		xbThrow xbEstream(this,STG_E_INVALIDPARAMETER,"Seek failed, xbSeekMode mode invalid");
	}
	if (p < 0 || p > long(xbNptr(m_pDataEnd) - xbNptr(m_pData))) 
		xbThrow xbEstream(this,STG_E_SEEKERROR,"Seek failed, position < 0 or position > data length");
	m_pCurPos = (uchar*)m_pData + p;
	
	return p;
}

ulong			xbConstStream::SetPosition(ulong pos)
{
	long p = pos;
	if (p < 0 || p > long(xbNptr(m_pDataEnd) - xbNptr(m_pData))) 
		xbThrow xbEstream(this,STG_E_SEEKERROR,"SetPosition failed, position too big");
	m_pCurPos = (uchar*)m_pData+p;
	
	return p;
}

void			xbConstStream::SetLength(ulong sz)
{
	xbThrow xbEstream(this,STG_E_ACCESSDENIED,"SetLength failed, not allowed for this type of stream, length is constant");
}

void			xbConstStream::SetEof()
{
	xbThrow xbEstream(this,STG_E_ACCESSDENIED,"SetEof failed, not allowed for this type of stream, length is constant");
}

xbString		xbFileStream::GetName() const 
{
	return "xbFileStream["+::xbEscape(m_FileName)+"]";
}
xbString		xbTempStream::GetName() const 
{
	return "xbTempStream["+::xbEscape(m_FileName)+"]";
}

ulong			xbStreamWithCache::Read(void *pData,ulong DataLg,bool DataLg_Required)
{
	xbLock	lck;
	if (ioMTlock())
		lck.Lock(this);

	if (!CanRead())
		xbThrow xbEstream(this,STG_E_ACCESSDENIED,"Read failed, permission denied");

	if (!m_cache.m_bufferSize)
	{
		if (!lck.IsLocked())
			lck.Lock(this);
		ulong ret = noCacheRead((uchar*)pData,DataLg);
		if (DataLg_Required && ret < DataLg)
			throwStreamReadEnd("Read failed, size requested not available");
		return ret;
	}
	if (m_cache.m_mode == xbStreamWithCacheMode_Write)
	{
		if (!lck.IsLocked())
			lck.Lock(this);
		m_cache.bufferWriteFlush(this);
	}

	uchar *ptr = (uchar*)pData;
	ulong DataLgCur = DataLg,subret;
	while(DataLgCur>0)
	{
		subret = m_cache.bufferRead(ptr,DataLgCur);
		if (!subret)
		{
			if (!lck.IsLocked())
				lck.Lock(this);
			if (DataLgCur >= m_cache.m_bufferSize)
			{
				m_cache.bufferEmpty();
				subret = noCacheRead(ptr,DataLgCur);
				DataLgCur -= subret;
				if (DataLg_Required && DataLgCur)
					throwStreamReadEnd("Read failed, size requested not available");
				return DataLg - DataLgCur;
			}
			subret = noCacheRead(m_cache.bufferReadFeed(),m_cache.m_bufferSize);
			if (!subret)
			{
				if (DataLg_Required)
					throwStreamReadEnd("Read failed, size requested not available");
				break;
			}
			m_cache.bufferReadFeedLength(subret);
			continue;
		}
		DataLgCur -= subret;
		ptr += subret;
	}

	return DataLg - DataLgCur;
}

ulong			xbStreamWithCache::Write(const void *pData,ulong DataLg,bool DataLg_Required)
{
	xbLock	lck;
	if (ioMTlock())
		lck.Lock(this);
	if (!CanWrite())
		xbThrow xbEstream(this,STG_E_ACCESSDENIED,"Write failed, permission denied");

	if (!m_cache.m_bufferSize)
	{
		if (!lck.IsLocked())
			lck.Lock(this);
		ulong ret = noCacheWrite((const uchar*)pData,DataLg);
		if (DataLg_Required && ret < DataLg)
			throwStreamWriteEnd("Write failed, size requested not available");
		return ret;
	}
	if (DataLg >= m_cache.m_bufferSize)
	{
		if (!lck.IsLocked())
			lck.Lock(this);
		if (m_cache.m_mode != xbStreamWithCacheMode_Noop)
		{
			if (m_cache.m_mode == xbStreamWithCacheMode_Write)
				m_cache.bufferWriteFlush(this);
			else
				m_cache.bufferReadReseek(this);
		}

		ulong ret = noCacheWrite((const uchar*)pData,DataLg);
		if (DataLg_Required && ret < DataLg)
			throwStreamWriteEnd("Write failed, size requested not available");
		return ret;
	}
	
	if (m_cache.m_mode == xbStreamWithCacheMode_Read)
		m_cache.bufferReadReseek(this);

	uchar *ptr = (uchar*)pData;
	ulong DataLgCur = DataLg,subret;
	while(DataLgCur>0)
	{
		subret = m_cache.bufferWrite(ptr,DataLgCur);
		if (!subret)
		{
			if (!lck.IsLocked())
				lck.Lock(this);
			m_cache.bufferWriteFlush(this,true);
			continue;
		}
		DataLgCur -= subret;
		ptr += subret;
	}

	return DataLg - DataLgCur;
}

void xbStreamWithCache::throwStreamReadEnd(const xbString &str)
{
	xbThrow xbEstream(this,STG_E_READFAULT,str);
}

void xbStreamWithCache::throwStreamWriteEnd(const xbString &str)
{
	xbThrow xbEstream(this,STG_E_WRITEFAULT,str);
}

IxbInputStream	&xbStreamWithCache::operator>>(xbStringA *pStr)
{
	internalReadStringA(xbFormatBin,this,pStr,NULL);
	return *this;
}

IxbInputStream	&xbStreamWithCache::operator>>(xbStringW *pStr)
{
	internalReadStringW(xbFormatBin,this,pStr,NULL);
	return *this;
}

IxbInputStream	&xbStreamWithCache::operator>>(xbBSTR *pStr)
{
	internalReadStringW(xbFormatBin,this,pStr,NULL);
	return *this;
}

ulong	xbStreamWithCache::ReadStringA(xbStringA *pStr,xbString *pStopStr,IxbStreamMode *pMode)
{
	return internalReadStringA(xbFormatText,pMode,pStr,pStopStr);
}

ulong	xbStreamWithCache::ReadStringW(xbStringW *pStr,xbStringW *pStopStr,IxbStreamMode *pMode)
{
	return internalReadStringW(xbFormatText,pMode,pStr,pStopStr);
}

ulong	xbStreamWithCache::ReadStringW(xbBSTR *pStr,xbBSTR *pStopStr,IxbStreamMode *pMode)
{
	return internalReadStringW(xbFormatText,pMode,pStr,pStopStr);
}

ulong xbStreamWithCache::internalReadStringA(xbFormatMode fmtDefault,IxbStreamMode *pMode,xbStringA *pStr,xbStringA *pStopStr)
{
	if (!pMode) 
		pMode = this;
	if (!m_cache.m_bufferSize)
		return xbStream_ReadStringA(fmtDefault,this,pMode,this,this,pStr,pStopStr);
	
	xbLock	lck;
	if (ioMTlock())
		lck.Lock(this);
	xbFormatMode fmt(pMode->GetFormat());
	if (fmt == xbFormatUndef)
		fmt = fmtDefault;

	switch(fmt)
	{
	case xbFormatBin :
		{
			long lg = ReadLong();
			if (lg <= 0)
			{
				pStr->Empty();
				return sizeof(long);
			}
			return sizeof(long) + Read((char*)xbStrExpA(pStr,lg,false),lg * sizeof(char));
		}
	case xbFormatText :
		if (xbTextBOMisUtf16(pMode->GetTextBOMtype()))
		{
			xbStringW str,strStop;
			ulong ret = internalReadStringTextW(&str,pStopStr?&strStop:NULL,pMode,&lck);
			if (pStopStr)
				*pStopStr = xbStringA(pMode->GetAppTextCP(),strStop);
			*pStr = xbStringA(pMode->GetAppTextCP(),str);
			return ret;
		}
		if (pMode->GetAppTextCP() != pMode->GetStreamTextCP())
		{
			xbStringA str,strStop;
			ulong ret = internalReadStringTextA(&str,pStopStr?&strStop:NULL,pMode,&lck);
			if (pStopStr)
				::xbCPconvert(pMode->GetStreamTextCP(),strStop,pMode->GetAppTextCP(),pStopStr);
			::xbCPconvert(pMode->GetStreamTextCP(),str,pMode->GetAppTextCP(),pStr);
			return ret;
		}
		return internalReadStringTextA(pStr,pStopStr,pMode,&lck);
	default:
		xbThrowEsys(xbHR_NOTIMPL,"xbStreamWithCache::internalReadStringA() :  format not supported");
	}
	return -1;//Never executed
}

ulong xbStreamWithCache::internalReadStringW(xbFormatMode fmtDefault,IxbStreamMode *pMode,xbStringW *pStr,xbStringW *pStopStr)
{
	if (!pMode) 
		pMode = this;
	if (!m_cache.m_bufferSize)
		return xbStream_ReadStringW(fmtDefault,this,pMode,this,this,pStr,pStopStr);
	
	xbLock	lck;
	if (ioMTlock())
		lck.Lock(this);
	xbFormatMode fmt(pMode->GetFormat());
	if (fmt == xbFormatUndef)
		fmt = fmtDefault;

	switch(fmt)
	{
	case xbFormatBin :
		{
			long lg = ReadLong();
			if (lg <= 0)
			{
				pStr->Empty();
				return sizeof(long);
			}
			return sizeof(long) + Read((wchar*)xbStrExpW(pStr,lg,false),lg * sizeof(wchar));
		}
	case xbFormatText :
		if (!pMode || !xbTextBOMisUtf16(pMode->GetTextBOMtype()))
		{
			xbStringA str,strStop;
			ulong ret = internalReadStringTextA(&str,pStopStr?&strStop:NULL,pMode,&lck);
			if (pStopStr)
				*pStopStr = xbStringW(pMode->GetStreamTextCP(),strStop);
			*pStr = xbStringW(pMode->GetStreamTextCP(),strStop);
			return ret;
		}
		return internalReadStringTextW(pStr,pStopStr,pMode,&lck);
	default:
		xbThrowEsys(xbHR_NOTIMPL,"xbStreamWithCache::internalReadStringW() :  format not supported");
	}
	return -1;//Never executed
}

ulong xbStreamWithCache::internalReadStringW(xbFormatMode fmtDefault,IxbStreamMode *pMode,xbBSTR *pStr,xbBSTR *pStopStr)
{
	if (!pMode) 
		pMode = this;
	if (!m_cache.m_bufferSize)
		return xbStream_ReadStringW(fmtDefault,this,pMode,this,this,pStr,pStopStr);
	
	xbLock	lck;
	if (ioMTlock())
		lck.Lock(this);
	xbFormatMode fmt(pMode->GetFormat());
	if (fmt == xbFormatUndef)
		fmt = fmtDefault;

	switch(fmt)
	{
	case xbFormatBin :
		{
			long lg = ReadLong();
			if (lg <= 0)
			{
				pStr->Empty();
				return sizeof(long);
			}
			return sizeof(long) + Read(pStr->GetWritePtr(lg),lg* sizeof(wchar));
		}
	case xbFormatText :
		if (!pMode || !xbTextBOMisUtf16(pMode->GetTextBOMtype()))
		{
			xbStringA str,strStop;
			ulong ret = internalReadStringTextA(&str,pStopStr?&strStop:NULL,pMode,&lck);
			if (pStopStr)
				*pStopStr = xbBSTR(xbAllocLib,pMode->GetStreamTextCP(),strStop);
			*pStr = xbBSTR(xbAllocLib,pMode->GetStreamTextCP(),strStop);
			return ret;
		}
		return internalReadStringTextW(pStr,pStopStr,pMode,&lck);
	default:
		xbThrowEsys(xbHR_NOTIMPL,"xbStreamWithCache::internalReadStringW() :  format not supported");
	}
	return -1;//Never executed
}

ulong	xbStreamWithCache::internalReadStringTextA(xbStringA *pStr,xbStringA *pStopStr,IxbStreamMode *pMode,xbLock *pLck)
{
	if (pStr)
		pStr->Empty();

	if (m_cache.m_mode == xbStreamWithCacheMode_Write)
	{
		if (!pLck->IsLocked())
			pLck->Lock(this);
		m_cache.bufferWriteFlush(this);
	}

	ulong cbrd = 0;
	for(;;)
	{
		ulong cb = m_cache.bufferReadBusyLength();
		if (!cb)
		{
			if (!pLck->IsLocked())
				pLck->Lock(this);
			cb = noCacheRead(m_cache.bufferReadFeed(),m_cache.m_bufferSize);
			if (!cb)
			{
				if (pStopStr) 
					pStopStr->Empty();
				return cbrd;
			}
			m_cache.bufferReadFeedLength(cb);
			continue;
		}

		const char *pBuf = (const char*)m_cache.bufferReadBusy();
		long seplg = 0;
		ulong seppos = pMode->FindLineSepA(1,pBuf,cb,&seplg);

		if (seplg<0)
		{
			//cas du marker trouvé en partie enfin de buffer-> recommencer la lecture à partir du début du marker probable sauf en cas de fin de fichier
			if (seppos > 1)
				cb = seppos-1;
			else
			{
				if (cb < m_cache.m_bufferSize)
				{
					uchar *ptr;
					ulong staying = m_cache.bufferReadComplete(&ptr,&cb);
					if (!pLck->IsLocked())
						pLck->Lock(this);
					cb = noCacheRead(ptr,cb);
					if (!cb)
					{
						if (pStopStr) pStopStr->Empty();
						return cbrd;
					}
					m_cache.bufferReadFeedLength(staying+cb);
					continue;
				}
				seplg = 0;
			}
			seppos = 0;
		}

		if (seppos)
		{
			if (pStr)
				pStr->Add(pBuf,seppos-1);
			cbrd += seppos - 1;
			if (pStopStr)
			{
				if (pMode && pMode->CRLFasLF && seplg == 2 && pBuf[seppos-1] == '\r'
					&& pBuf[seppos] == '\n') 
					pStopStr->Set("\n",1);
				else 
					pStopStr->Set(pBuf+seppos-1,seplg);
			}
			m_cache.bufferReadBusyDone(seppos+seplg-1);
			
			return cbrd + seplg;
		}
		
		if (pStr)
			pStr->Add(pBuf,cb);
		cbrd += cb;
		m_cache.bufferReadBusyDone(cb);
		if (seplg >= 0)
			continue;

		uchar *ptr;
		ulong staying = m_cache.bufferReadComplete(&ptr,&cb);
		if (!pLck->IsLocked())
			pLck->Lock(this);
		cb = noCacheRead(ptr,cb);
		if (!cb)
		{
			if (pStopStr) pStopStr->Empty();
			return cbrd;
		}
		m_cache.bufferReadFeedLength(staying+cb);
	}

	return cbrd;
}

ulong	xbStreamWithCache::internalReadStringTextW(xbStringW *pStr,xbStringW *pStopStr,IxbStreamMode *pMode,xbLock *pLck)
{
	if (pStr)
		pStr->Empty();

	if (m_cache.m_mode == xbStreamWithCacheMode_Write)
	{
		if (!pLck->IsLocked())
			pLck->Lock(this);
		m_cache.bufferWriteFlush(this);
	}

	ulong cbrd = 0;
	for(;;)
	{
		ulong cb = m_cache.bufferReadBusyLength();
		if (!cb)
		{
			if (!pLck->IsLocked())
				pLck->Lock(this);
			cb = noCacheRead(m_cache.bufferReadFeed(),m_cache.m_bufferSize);
			if (!cb)
			{
				if (pStopStr) 
					pStopStr->Empty();
				return cbrd;
			}
			m_cache.bufferReadFeedLength(cb);
			continue;
		}
		cb /= sizeof(wchar);
		if (!cb)
		{
			uchar *ptr;
			ulong staying = m_cache.bufferReadComplete(&ptr,&cb);
			if (!pLck->IsLocked())
				pLck->Lock(this);
			cb = noCacheRead(ptr,cb);
			if (!cb)
			{
				if (pStopStr) 
					pStopStr->Empty();
				return cbrd;
			}
			m_cache.bufferReadFeedLength(staying+cb);
			continue;
		}

		const wchar *pBuf = (const wchar*)m_cache.bufferReadBusy();
		long seplg = 0;
		ulong seppos = pMode->FindLineSepW(1,pBuf,cb,&seplg);

		if (seplg<0)
		{
			//cas du marker trouvé en partie enfin de buffer-> recommencer la lecture à partir du début du marker probable sauf en cas de fin de fichier
			if (seppos > 1)
				cb = seppos-1;
			else
			{
				if (cb < (m_cache.m_bufferSize/sizeof(wchar)))
				{
					uchar *ptr;
					ulong staying = m_cache.bufferReadComplete(&ptr,&cb);
					if (!pLck->IsLocked())
						pLck->Lock(this);
					cb = noCacheRead(ptr,cb);
					if (!cb)
					{
						if (pStopStr) 
							pStopStr->Empty();
						return cbrd;
					}
					m_cache.bufferReadFeedLength(staying+cb);
					continue;
				}
				seplg = 0;
			}
			seppos = 0;
		}

		if (seppos)
		{
			if (pStr)
				pStr->Add(pBuf,seppos-1);
			cbrd += seppos - 1;
			if (pStopStr)
			{
				if (pMode && pMode->CRLFasLF && seplg == 2 && pBuf[seppos-1] == L'\r'
					&& pBuf[seppos] == L'\n') 
					pStopStr->Set(L"\n",1);
				else 
					pStopStr->Set(pBuf+seppos-1,seplg);
			}
			m_cache.bufferReadBusyDone((seppos+seplg-1)*sizeof(wchar));
			return cbrd + seplg;
		}
		
		if (pStr)
			pStr->Add(pBuf,cb);
		cbrd += cb;
		m_cache.bufferReadBusyDone(cb);
		if (seplg >= 0)
			continue;

		uchar *ptr;
		ulong staying = m_cache.bufferReadComplete(&ptr,&cb);
		if (!pLck->IsLocked())
			pLck->Lock(this);
		cb = noCacheRead(ptr,cb);
		if (!cb)
		{
			if (pStopStr) 
				pStopStr->Empty();
			return cbrd;
		}
		m_cache.bufferReadFeedLength(staying+cb);
	}

	if (pStopStr) 
		pStopStr->Empty();
	return cbrd;
}

ulong	xbStreamWithCache::internalReadStringTextW(xbBSTR *pStr,xbBSTR *pStopStr,IxbStreamMode *pMode,xbLock *pLck)
{
	if (pStr)
		pStr->Empty();

	if (m_cache.m_mode == xbStreamWithCacheMode_Write)
	{
		if (!pLck->IsLocked())
			pLck->Lock(this);
		m_cache.bufferWriteFlush(this);
	}

	ulong cbrd = 0;
	for(;;)
	{
		ulong cb = m_cache.bufferReadBusyLength();
		if (!cb)
		{
			if (!pLck->IsLocked())
				pLck->Lock(this);
			cb = noCacheRead(m_cache.bufferReadFeed(),m_cache.m_bufferSize);
			if (!cb)
			{
				if (pStopStr) 
					pStopStr->Empty();
				return cbrd;
			}
			m_cache.bufferReadFeedLength(cb);
			continue;
		}
		cb /= sizeof(wchar);
		if (!cb)
		{
			uchar *ptr;
			ulong staying = m_cache.bufferReadComplete(&ptr,&cb);
			if (!pLck->IsLocked())
				pLck->Lock(this);
			cb = noCacheRead(ptr,cb);
			if (!cb)
			{
				if (pStopStr) 
					pStopStr->Empty();
				return cbrd;
			}
			m_cache.bufferReadFeedLength(staying+cb);
			continue;
		}

		const wchar *pBuf = (const wchar*)m_cache.bufferReadBusy();
		long seplg = 0;
		ulong seppos = pMode->FindLineSepW(1,pBuf,cb,&seplg);

		if (seplg<0)
		{
			//cas du marker trouvé en partie enfin de buffer-> recommencer la lecture à partir du début du marker probable sauf en cas de fin de fichier
			if (seppos > 1)
				cb = seppos-1;
			else
			{
				if (cb < (m_cache.m_bufferSize/sizeof(wchar)))
				{
					uchar *ptr;
					ulong staying = m_cache.bufferReadComplete(&ptr,&cb);
					if (!pLck->IsLocked())
						pLck->Lock(this);
					cb = noCacheRead(ptr,cb);
					if (!cb)
					{
						if (pStopStr) 
							pStopStr->Empty();
						return cbrd;
					}
					m_cache.bufferReadFeedLength(staying+cb);
					continue;
				}
				seplg = 0;
			}
			seppos = 0;
		}

		if (seppos)
		{
			if (pStr)
				pStr->Add(pBuf,seppos-1);
			cbrd += seppos - 1;
			if (pStopStr)
			{
				if (pMode && pMode->CRLFasLF && seplg == 2 && pBuf[seppos-1] == L'\r'
					&& pBuf[seppos] == L'\n') 
					pStopStr->Set(L"\n",1);
				else 
					pStopStr->Set(pBuf+seppos-1,seplg);
			}
			m_cache.bufferReadBusyDone((seppos+seplg-1)*sizeof(wchar));
			return cbrd + seplg;
		}
		
		if (pStr)
			pStr->Add(pBuf,cb);
		cbrd += cb;
		m_cache.bufferReadBusyDone(cb);
		if (seplg >= 0)
			continue;

		uchar *ptr;
		ulong staying = m_cache.bufferReadComplete(&ptr,&cb);
		if (!pLck->IsLocked())
			pLck->Lock(this);
		cb = noCacheRead(ptr,cb);
		if (!cb)
		{
			if (pStopStr) 
				pStopStr->Empty();
			return cbrd;
		}
		m_cache.bufferReadFeedLength(staying+cb);
	}

	if (pStopStr) 
		pStopStr->Empty();
	return cbrd;
}

void xbStreamWithCacheSt::bufferReadReseek(xbStreamWithCache *pStm)
{
	xbRdbgAssert(m_mode == xbStreamWithCacheMode_Read);
	m_mode = xbStreamWithCacheMode_Noop;
	ulong staying = m_bufferBusyLg - m_bufferBusyPos;
	m_bufferBusyLg = 0;
	m_bufferBusyPos = 0;
	if (staying)
	{
		pStm->Seek(-long(staying),xbSeekCur);
	}
}

void xbStreamWithCacheSt::bufferWriteFlush(xbStreamWithCache *pStm,bool bWriteContinue)
{
	xbRdbgAssert(m_mode == xbStreamWithCacheMode_Write);
	if (m_bufferBusyPos)
	{
		ulong ret = pStm->noCacheWrite(m_pBuffer,m_bufferBusyPos);
		if (ret != m_bufferBusyPos)
			pStm->throwStreamWriteEnd("Write failed, size requested not available");
		m_bufferBusyPos =0;
	}
	if (bWriteContinue)
		return;
	m_mode = xbStreamWithCacheMode_Noop;
}

void xbStreamWithCache::SetCacheSize(ulong cByte)
{
	xbLock lck(this);
	if (m_cache.m_bufferSize == cByte)
		return;
	if (m_cache.m_mode != xbStreamWithCacheMode_Noop)
	{
		if (m_cache.m_mode == xbStreamWithCacheMode_Write)
			m_cache.bufferWriteFlush(this);
		else
			m_cache.bufferReadReseek(this);
	}
	m_cache.bufferFree();
	m_cache.m_bufferSize = cByte;
}

void xbStreamWithCache::CacheFlush()
{
	xbLock lck(this);
	if (m_cache.m_mode == xbStreamWithCacheMode_Write)
		m_cache.bufferWriteFlush(this);
}

void xbStreamWithCache::CacheFlushRW(bool bNoReadSeek)
{
	xbLock lck(this);
	if (m_cache.m_mode != xbStreamWithCacheMode_Noop)
	{
		if (m_cache.m_mode == xbStreamWithCacheMode_Write)
			m_cache.bufferWriteFlush(this);
		else
		{
			if (bNoReadSeek)
				m_cache.bufferEmpty();
			else
				m_cache.bufferReadReseek(this);
		}
	}
}

xbString xbStream::GetStreamContentType() const
{
	return g_xbCfgEngine.GetContentTypeFromPath(GetPhysicalName(),xbOp_Optional);
}

const void *xbConstStream::GetData(xbint *pcByte) 
{
	if (pcByte) 
		*pcByte = xbint(xbNptr(m_pDataEnd) - xbNptr(m_pData));
	return m_pData;
}

ulong			xbConstStream::GetPosition() const		
{
	return ulong(xbNptr(m_pCurPos) - xbNptr(m_pData));
}

ulong			xbConstStream::GetLength() const			
{
	return ulong(xbNptr(m_pDataEnd) - xbNptr(m_pData));
}

#pragma warning(pop)
