//
// MODULE: FILEREAD.CPP
//
// PURPOSE: file reading classes
//
// COMPANY: Saltmine Creative, Inc. (206)-284-7511 support@saltmine.com
//
// AUTHOR: Oleg Kalosha
// 
// ORIGINAL DATE: 7-29-98
//
// NOTES: 
//
// Version	Date		By		Comments
//--------------------------------------------------------------------
// V3.0		08-04-98	OK
//

#include "stdafx.h"
#include <algorithm>
#include "fileread.h"
#include "event.h"
#include "CharConv.h"
#include "apgtsassert.h"
#ifdef LOCAL_TROUBLESHOOTER
#include "CHMFileReader.h"
#endif

#define STR_ALLOC_SIZE  1024
#define FORWARD_SLASH	_T('/')
#define BACK_SLASH      _T('\\')


////////////////////////////////////////////////////////////////////////////////////
// CFileReaderException
////////////////////////////////////////////////////////////////////////////////////
// source_file is LPCSTR rather than LPCTSTR because __FILE__ is char[35]
CFileReaderException::CFileReaderException(CPhysicalFileReader* reader, eErr err, LPCSTR source_file, int line)
					: CBaseException(source_file, line),
					  m_pFileReader(reader),
					  m_eErr(err)
{
}

CFileReaderException::CFileReaderException(CFileReader* reader, eErr err, LPCSTR source_file, int line)
					: CBaseException(source_file, line),
					  m_pFileReader(reader->GetPhysicalFileReader()),
					  m_eErr(err)
{
}


CFileReaderException::~CFileReaderException()
{
}

void CFileReaderException::CloseFile()
{
	if (m_eErr == eErrClose || m_eErr == eErrGetSize || m_eErr == eErrRead || m_eErr == eErrAllocateToRead)
	   m_pFileReader->CloseHandle();
}

void CFileReaderException::LogEvent() const
{
	CBuildSrcFileLinenoStr CatchLoc( __FILE__, __LINE__ );
	CString	strErr;

	// Format the error code as a string.
	switch (m_eErr)
	{
		case eErrOpen: 
				strErr= _T("Open"); 
				break;
		case eErrClose: 
				strErr= _T("Close"); 
				break;
		case eErrRead: 
				strErr= _T("Read"); 
				break;
		case eErrAllocateToRead: 
				strErr= _T("ReadAllocate"); 
				break;
		case eErrGetSize: 
				strErr= _T("GetSize"); 
				break;
		case eErrGetDateTime: 
				strErr= _T("GetDateTime"); 
				break;
		case eErrParse: 
				strErr= _T("Parse"); 
				break;
		default:
				strErr.Format( _T("Error code of %d"), m_eErr );
	}

	CEvent::ReportWFEvent(	GetSrcFileLineStr(), 
							CatchLoc.GetSrcFileLineStr(), 
							strErr, 
							m_pFileReader->GetNameToLog(), 
							EV_GTS_FILEREADER_ERROR );
}

////////////////////////////////////////////////////////////////////////////////////
// CAbstractFileReader
// This class manages a file, which is initially read into a memory buffer, then
//	copied into a stream.
// It must be further specialized to handle a file from ordinary disk storage vs. a 
//	file from a CHM
////////////////////////////////////////////////////////////////////////////////////
// we return just pure path, without <name>.<ext> and without slashes in the tail
/*static*/ CString CAbstractFileReader::GetJustPath(const CString& full_path)
{
	CString tmp = full_path;

	tmp.TrimLeft();
	tmp.TrimRight();

	int indexOfSlash = tmp.ReverseFind(BACK_SLASH);

	if (indexOfSlash == -1)
		indexOfSlash = tmp.ReverseFind(FORWARD_SLASH);

	if (indexOfSlash == -1)
		// Unable to locate the path, return an empty string.
		return _T(""); 
	else
		return tmp.Left(indexOfSlash);
}

// we return just <name>.<ext> without any path information.  If there's no slash and no dot
//	anywhere, we presume this is not a file name.
/*static*/ CString CAbstractFileReader::GetJustName(const CString& full_path)
{
	CString tmp = full_path;
	LPTSTR ptr = NULL;

	tmp.TrimLeft();
	tmp.TrimRight();

	int indexOfSlash = tmp.ReverseFind(BACK_SLASH);

	if (indexOfSlash == -1)
		indexOfSlash = tmp.ReverseFind(FORWARD_SLASH);

	if (indexOfSlash == -1)
	{
		if (tmp.Find("."))
			return tmp; // full_path is a file name
		else
			// Unable to detect a file name, return an empty string.
			return _T(""); 
	}
	else
		return tmp.Mid(indexOfSlash + 1);
}

/*static*/ CString CAbstractFileReader::GetJustNameWithoutExtension(const CString& full_path)
{
	CString tmp = GetJustName(full_path);
	int point = tmp.Find(_T('.'));

	if (-1 != point)
		return tmp.Left(point);
	return tmp;
}

/*static*/ CString CAbstractFileReader::GetJustExtension(const CString& full_path)
{
	CString tmp = GetJustName(full_path);
	int point = tmp.Find(_T('.'));

	if (-1 != point)
		return tmp.Right(tmp.GetLength() - point - 1);
	return _T("");
}

/*static*/ bool CAbstractFileReader::GetFileTime(const CString& full_path, EFileTime type, time_t& out)
{
	WIN32_FIND_DATA find_data;
	FILETIME fileTime, localTime;
    SYSTEMTIME sysTime;
    struct tm atm;
	HANDLE hLocFile;
	bool bRet= false;
	
	hLocFile= ::FindFirstFile(full_path, &find_data);
	if (INVALID_HANDLE_VALUE == hLocFile)
		return( bRet );

	if (type == eFileTimeCreated)
		fileTime = find_data.ftCreationTime;
	if (type == eFileTimeModified)
		fileTime = find_data.ftLastWriteTime;
	if (type == eFileTimeAccessed)
		fileTime = find_data.ftLastAccessTime;

    // first convert file time (UTC time) to local time
    if (::FileTimeToLocalFileTime(&fileTime, &localTime)) 
	{
	    // then convert that time to system time
		if (::FileTimeToSystemTime(&localTime, &sysTime))
		{
			if (!(sysTime.wYear < 1900))
			{
				atm.tm_sec = sysTime.wSecond;
				atm.tm_min = sysTime.wMinute;
				atm.tm_hour = sysTime.wHour;
				ASSERT(sysTime.wDay >= 1 && sysTime.wDay <= 31);
				atm.tm_mday = sysTime.wDay;
				ASSERT(sysTime.wMonth >= 1 && sysTime.wMonth <= 12);
				atm.tm_mon = sysTime.wMonth - 1; // tm_mon is 0 based
				ASSERT(sysTime.wYear >= 1900);
				atm.tm_year = sysTime.wYear - 1900; // tm_year is 1900 based
				atm.tm_isdst = -1; // automatic computation of daylight saving time
				out = mktime(&atm);
				bRet= true;
			}
		}
	}

	::FindClose( hLocFile );

	return( bRet );
}

CAbstractFileReader::CAbstractFileReader()
		   : CStateless(),
			 m_bIsValid(true),
			 m_bIsRead(false)
{
}

CAbstractFileReader::~CAbstractFileReader()
{
}

// returns true if the referenced file can be opened and closed.  
// No problem if the file is already open: it is opened with FILE_SHARE_READ access.
bool CAbstractFileReader::Exists()
{
	bool bRet= false;

	try 
	{
		LOCKOBJECT();
		Open();
		Close();
		bRet= true;
	}
	catch (CFileReaderException& exc) 
	{
		exc.CloseFile();
		exc.LogEvent();
	}
	catch (...)
	{
		// Catch any other exception thrown.
		CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
		CEvent::ReportWFEvent(	SrcLoc.GetSrcFileLineStr(), 
								SrcLoc.GetSrcFileLineStr(), 
								_T(""), _T(""), 
								EV_GTS_GEN_EXCEPTION );		
	}
	UNLOCKOBJECT();

	return( bRet );
}

// go back to the file itself for data.
bool CAbstractFileReader::Read()
{
	LPTSTR pBuf= NULL;		 // if non-null, points to an allocated buffer containing
							 // an in-memory copy of this file.
	try 
	{
		LOCKOBJECT();
		Open();
		ReadData(&pBuf);
		Close();
		StreamData(&pBuf);
		Parse(); // if this parsing is OK, the parsing in all siblings is presumed to be OK
		m_bIsRead = true;
		m_bIsValid = true;
	}
	catch (CFileReaderException& exc) 
	{
		exc.CloseFile();
		m_bIsValid = false;
		try 
		{
			if (UseDefault())
			{
				Parse(); // if this parsing is OK, the parsing in all siblings is presumed to be OK
				m_bIsRead = true;		// OK, so  maybe we're lying.  Close enough to true.
				m_bIsValid = true;
			}
		}
		catch (CFileReaderException&) 
		{
			// Catch any potential exceptions from attempt to access default content.
			// This exception would be logged below so there is no need to log it here.
		}

		if (!m_bIsValid)
		{
			// Only log the event if the attempt to access default content failed.
			exc.LogEvent();
		}
	}
	catch (bad_alloc&)
	{
		// Memory allocation failure.
		m_bIsValid = false;
		CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
		CEvent::ReportWFEvent(	SrcLoc.GetSrcFileLineStr(), 
								SrcLoc.GetSrcFileLineStr(), 
								_T(""), _T(""), EV_GTS_CANT_ALLOC ); 
	}
	catch (...)
	{
		// Catch any other exception thrown.
		CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
		CEvent::ReportWFEvent(	SrcLoc.GetSrcFileLineStr(), 
								SrcLoc.GetSrcFileLineStr(), 
								_T(""), _T(""), 
								EV_GTS_GEN_EXCEPTION );		
	}

	if (pBuf)
		delete [] pBuf;

	// Given the array of catch blocks above, it is assumed that this call to unlock the
	//	object will always been called prior to exiting this function.
	UNLOCKOBJECT();

	return m_bIsValid;
}

////////////////////////////////////////////////////////////////////////////////////
// CPhysicalFileReader
////////////////////////////////////////////////////////////////////////////////////
CPhysicalFileReader::CPhysicalFileReader()
{
}

CPhysicalFileReader::~CPhysicalFileReader()
{
}

/*static*/ CPhysicalFileReader * CPhysicalFileReader::makeReader( const CString& strFileName )
{
#ifdef LOCAL_TROUBLESHOOTER
	if (CCHMFileReader::IsCHMfile( strFileName ))
		return dynamic_cast<CPhysicalFileReader*>(new CCHMFileReader( strFileName ));
	else
#endif
		return dynamic_cast<CPhysicalFileReader*>(new CNormalFileReader( strFileName ));
}

////////////////////////////////////////////////////////////////////////////////////
// CNormalFileReader
// This class manages a file from ordinary storage.
// Do not use this for files within a CHM
////////////////////////////////////////////////////////////////////////////////////
CNormalFileReader::CNormalFileReader(LPCTSTR path)
		   : m_strPath(path),
			 m_hFile(NULL)
{
}

CNormalFileReader::~CNormalFileReader()
{
}

/* virtual */ void CNormalFileReader::Open()
{
	if (INVALID_HANDLE_VALUE == 
		(m_hFile = ::CreateFile( m_strPath, 
							 GENERIC_READ, 
							 FILE_SHARE_READ, 
							 NULL,
							 OPEN_EXISTING,
							 FILE_ATTRIBUTE_NORMAL,
							 NULL )) )
	{
#ifdef _DEBUG
		DWORD err = GetLastError();
#endif
		throw CFileReaderException( this, CFileReaderException::eErrOpen, __FILE__, __LINE__ );
	}
}

// returns true on success
// doesn't throw exception, therefore may be used by exception class.
/* virtual */ bool CNormalFileReader::CloseHandle()
{
	// if it's not open, say we closed successfully.
	if (!m_hFile)
		return true;

	return ::CloseHandle(m_hFile) ? true : false;
}


/* virtual */ void CNormalFileReader::ReadData(LPTSTR * ppBuf)
{
	DWORD dwSize =0, dwRead =0;

	if (*ppBuf) 
	{
		delete [] *ppBuf;
		*ppBuf = NULL;
	}

	if (0xFFFFFFFF == (dwSize = ::GetFileSize(m_hFile, NULL)))
	{
		throw CFileReaderException(this, CFileReaderException::eErrOpen, __FILE__, __LINE__);
	}

	// Handle this memory allocation like all others in the program.
	try
	{
		*ppBuf = new TCHAR[dwSize+1];
		//[BC-03022001] - addd check for NULL ptr to satisfy MS code analysis tool.
		if(!*ppBuf)
			throw bad_alloc();
	}
	catch (bad_alloc&)
	{
		throw CFileReaderException(this, CFileReaderException::eErrAllocateToRead, __FILE__, __LINE__);
	}

	if (!::ReadFile(m_hFile, *ppBuf, dwSize, &dwRead, NULL) || dwSize != dwRead)
	{
		throw CFileReaderException(this, CFileReaderException::eErrRead, __FILE__, __LINE__);
	}
		
	(*ppBuf)[dwSize] = 0;
}

CString CNormalFileReader::GetJustPath() const
{
	return CAbstractFileReader::GetJustPath(m_strPath);
}

CString CNormalFileReader::GetJustName() const
{
	return CAbstractFileReader::GetJustName(m_strPath);
}

CString CNormalFileReader::GetJustNameWithoutExtension() const
{
	return CAbstractFileReader::GetJustNameWithoutExtension(m_strPath);
}

CString CNormalFileReader::GetJustExtension() const
{
	return CAbstractFileReader::GetJustExtension(m_strPath);
}

bool CNormalFileReader::GetFileTime(CAbstractFileReader::EFileTime type, time_t& out) const
{
	return CAbstractFileReader::GetFileTime(m_strPath, type, out);
}

// name to log on exceptions.  This implementation will be correct for the normal file system,
//	but may need to be overridden for CHM.
CString CNormalFileReader::GetNameToLog() const
{
	return GetPathName();
}

////////////////////////////////////////////////////////////////////////////////////
// CFileReader
// This class manages a file, which is initially read into a memory buffer, then
//	copied into a stream.
////////////////////////////////////////////////////////////////////////////////////

CFileReader::CFileReader(CPhysicalFileReader * pPhysicalFileReader, bool bDeletePhysicalFileReader /*=true*/)
		   : CAbstractFileReader(),
			 m_pPhysicalFileReader(pPhysicalFileReader),
			 m_bDeletePhysicalFileReader(bDeletePhysicalFileReader)
{
}

CFileReader::~CFileReader()
{
	if (m_pPhysicalFileReader)
		if (m_bDeletePhysicalFileReader)
			delete m_pPhysicalFileReader;
}

// move the data out of ppBuf (which will be deleted) to m_StreamData
/* virtual */ void CFileReader::StreamData(LPTSTR * ppBuf)
{
	m_StreamData.str(*ppBuf);
	delete [] (*ppBuf);
	*ppBuf = NULL;
}

// Placeholder.  Classes that inherit from CFileReader can define parsing to happen
//	immediately after the file is read.
/* virtual */ void CFileReader::Parse()
{
	// we have no idea how to parse here
}

// Placeholder.  Classes that inherit from CFileReader can define default file contents
//	to use if file can't be read or what is read can't be parsed.
// Should return true if there's a default to use.
/* virtual */ bool CFileReader::UseDefault()
{
	// we have no default to use here
	return false;
}

void CFileReader::Close()
{
	if (!m_pPhysicalFileReader->CloseHandle())
		throw CFileReaderException(m_pPhysicalFileReader, CFileReaderException::eErrClose, __FILE__, __LINE__);
}

// Data access in form of tstring.  returns reference to its argument as a convenience.
tstring& CFileReader::GetContent(tstring& out)
{
	out = m_StreamData.rdbuf()->str();
	return out;
}

// Data access in form of CString.  returns reference to its argument as a convenience.
CString& CFileReader::GetContent(CString& out)
{
	out = m_StreamData.rdbuf()->str().c_str();
	return out;
}

////////////////////////////////////////////////////////////////////////////////////
// CTextFileReader
// Specialize CFileReader to a text file
////////////////////////////////////////////////////////////////////////////////////
/*static*/ bool CTextFileReader::IsAmongSeparators(TCHAR separatorCandidate, const vector<TCHAR>& separator_arr)
{
	vector<TCHAR>::const_iterator res = find(separator_arr.begin(), separator_arr.end(), separatorCandidate);
	return res != separator_arr.end();
}

// OUTPUT out is a vector of "words"
// NOTE: words are strings that do not contain whitespaces
/*static*/ void CTextFileReader::GetWords(const CString& text, vector<CString>& out, const vector<TCHAR>& separator_arr)
{
	LPTSTR begin =(LPTSTR)(LPCTSTR)text, end =(LPTSTR)(LPCTSTR)text;

	while (*begin)
	{
		if (!IsAmongSeparators(*begin, separator_arr))
		{
			end = begin;
			while (*end &&
				   !IsAmongSeparators(*end, separator_arr)
				  )
				end++;
			if (end != begin)
			{
				try
				{
					TCHAR* buf= new TCHAR[end-begin+1]; 
					//[BC-03022001] - added check for NULL ptr to satisfy MS code analysis tool.
					if(buf)
					{
						_tcsncpy(buf, begin, end-begin);
						buf[end-begin] = 0;
						out.push_back(buf);
						delete [] buf;
					}
					else
					{
						throw bad_alloc();
					}
				}
				catch (bad_alloc&)
				{
					// Memory allocation failure, log it and rethrow exception.
					CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
					CEvent::ReportWFEvent(	SrcLoc.GetSrcFileLineStr(), 
											SrcLoc.GetSrcFileLineStr(), 
											_T(""), _T(""), EV_GTS_CANT_ALLOC ); 
					throw;
				}
				catch (exception& x)
				{
					CString str;
					// Note STL exception in event log and rethrow exception.
					CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
					CEvent::ReportWFEvent(	SrcLoc.GetSrcFileLineStr(), 
											SrcLoc.GetSrcFileLineStr(), 
											CCharConversion::ConvertACharToString(x.what(), str), 
											_T(""), 
											EV_GTS_STL_EXCEPTION ); 
					throw;
				}
			}
			if (!*end)
				end--;
			begin = end;

		}
		begin++;
	}
}

/*static*/ long CTextFileReader::GetPos(tistream& streamData)
{
	return streamData.tellg();
}

/*static*/ bool CTextFileReader::SetPos(tistream& streamData, long pos)
{
	long eof_pos = 0;
	long old_pos = streamData.tellg();
	bool eof_state = streamData.eof();

	streamData.seekg(0, ios_base::end);
	eof_pos = streamData.tellg();

	if (pos <= eof_pos)
	{
		if (eof_state)
			streamData.clear(~ios_base::eofbit & streamData.rdstate()); // clear eof bit
		streamData.seekg(pos);
		return true;
	}
	else
	{
		streamData.seekg(old_pos);
		return false;
	}
}

// It get line of text from current position in stream until '\r' or EOF - into "str"
//  Stream is positioned to the beginning of next line or to EOF.
/*static*/ bool CTextFileReader::GetLine(tistream& streamData, CString& str)
{
	bool	bRetVal= false;
	TCHAR	buf[ STR_ALLOC_SIZE ];
	
	str= _T("");
	while (!streamData.eof())
	{
		buf[STR_ALLOC_SIZE-1] = 1; // will be NULL if buffer is completely filled up

		{	// start getline block
			long before_getline_pos = GetPos(streamData);

			streamData.getline(buf, STR_ALLOC_SIZE, _T('\r'));

			if (streamData.fail()) 
			{   // getline ran into empty line, and at this point
				//  failbit is set, and current input pointer is set to -1
				//  we are trying to recover this, since there is nothing 
				//  extraodinary to have line empty
				streamData.clear(~ios_base::failbit & streamData.rdstate());

				// Check if buffer was filled up completely.  If so, we do not want
				//	to reposition the file pointer as this is a completely valid 
				//	situation.  We will output this piece of the line and then will
				//	grab the next piece of the line only appending a newline character 
				//	once we have read in the entire line.
				if (buf[STR_ALLOC_SIZE-1] != NULL ) // buf was not filled up completely
					streamData.seekg(before_getline_pos); // we do not use SetPos, since SetPos
													  //  might clear eofbit, but in this situation
													  //  we do not want it.
			}
		}	// end getline block

		if (streamData.eof())
		{
			str += buf;
			bRetVal= true;
			break;
		}
		else 
		{	
			TCHAR element = 0;

			str += buf;

			if (streamData.peek() == _T('\n')) 
			{	// LINE FEED is next
				streamData.get(element); // just extract it from stream
				if (ios_base::eofbit & streamData.rdstate())
				{
					bRetVal= true;
					break;
				}
			}
			else
			{   // it was a standing along '\r'...
				// Check if we have a full buffer, if so do not append a newline 
				//	character as we need to grab the rest of the line before appending
				//	the newline character.
				if (buf[STR_ALLOC_SIZE-1] != NULL ) // buf was not filled up completely
					str += _T("\n");
				continue;
			}

			if (buf[STR_ALLOC_SIZE-1] != NULL ) // buf was not filled up completely
			{
				bRetVal= true;
				break;
			}
		}	
	}

	return( bRetVal );
}

// This function finds string in the stream and positions stream to the beginning 
//  of the string if found.
//  "str" should not include '\r''\n' pairs
/*static*/ bool CTextFileReader::Find(tistream& streamData, const CString& str, bool from_stream_begin /*=true*/)
{
	CString buf;
	long savePos = 0, currPos = 0;
	
	savePos = GetPos(streamData);
	if (from_stream_begin)
		SetPos(streamData, 0);

	currPos = GetPos(streamData);
	while (GetLine(streamData, buf))
	{
		long inside_pos = 0;
		if (-1 != (inside_pos = buf.Find(str)))
		{
			SetPos(streamData, currPos + inside_pos);
			return true;
		}
		currPos = GetPos(streamData);
	}
	SetPos(streamData, savePos);
	return false;
}

/*static*/ bool CTextFileReader::NextLine(tistream& streamData)
{
	CString str;
	return GetLine(streamData, str);
}

/*static*/ bool CTextFileReader::PrevLine(tistream& streamData)
{
	long savePos = 0;
	
	savePos = GetPos(streamData);
	SetAtLineBegin(streamData);
	if (GetPos(streamData) > 1)
	{
		SetPos(streamData, GetPos(streamData) - 2L); // skip '\n' and '\r'
		SetAtLineBegin(streamData);
		return true;
	}
	SetPos(streamData, savePos);
	return false;
}

// Positions stream to the beginning of current line.
//  assume that we are NEVER positioned to point to '\n' or '\r'
/*static*/ void CTextFileReader::SetAtLineBegin(tistream& streamData)
{
	while (GetPos(streamData))
	{
		SetPos(streamData, GetPos(streamData) - 1L);
		if (streamData.peek() == _T('\n'))
		{
			if (GetPos(streamData))
			{
				SetPos(streamData, GetPos(streamData) - 1L);
				if (streamData.peek() == _T('\r'))
				{
					SetPos(streamData, GetPos(streamData) + 2L);
					return;
				}
			}
		}
	}
}

CTextFileReader::CTextFileReader(CPhysicalFileReader *pPhysicalFileReader, LPCTSTR szDefaultContents /* = NULL */, bool bDeletePhysicalFileReader /*=true*/ )
			   : CFileReader(pPhysicalFileReader, bDeletePhysicalFileReader),
				 m_strDefaultContents(szDefaultContents ? szDefaultContents : _T(""))
{
}

CTextFileReader::~CTextFileReader()
{
}

long CTextFileReader::GetPos()
{
	return GetPos(m_StreamData);
}

// this function is to be used instead of seekg
//  it clears eof flag if "pos" is not the last
//  position in the file.
bool CTextFileReader::SetPos(long pos)
{
	return SetPos(m_StreamData, pos);
}

bool CTextFileReader::GetLine(CString& str)
{
	return GetLine(m_StreamData, str);
}

bool CTextFileReader::Find(const CString& str, bool from_stream_begin /*=true*/)
{
	return Find(m_StreamData, str, from_stream_begin);
}

void CTextFileReader::SetAtLineBegin()
{
	SetAtLineBegin(m_StreamData);
}

bool CTextFileReader::NextLine()
{
	return NextLine(m_StreamData);
}

bool CTextFileReader::PrevLine()
{
	return PrevLine(m_StreamData);
}

bool CTextFileReader::UseDefault()
{
	if ( ! m_strDefaultContents.IsEmpty() )
	{
		m_StreamData.str((LPCTSTR)m_strDefaultContents);
		return true;
	}
	return false;
}