/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Class responsible to parse data files
*
*/


#include "../inc/CATDatParser.h"
#include "../inc/CATProject.h"
#include "../inc/CATModule2.h"
#include "../inc/CATMemoryAddress.h"
#include "../inc/catromsymbol.h"

// -----------------------------------------------------------------------------
// CATDatParser::CATDatParser
// Constructor only for testing!
// (No module vector defined so no locating codelines / call stacks)
// -----------------------------------------------------------------------------
CATDatParser::CATDatParser()
{
	LOG_FUNC_ENTRY("CATDatParser::CATDatParser");
	Construct();
}

// -----------------------------------------------------------------------------
// CATDatParser::CATDatParser
// Constructor
// -----------------------------------------------------------------------------
CATDatParser::CATDatParser(vector<CATModule2*>* pModules )
{
	LOG_FUNC_ENTRY("CATDatParser::CATDatParser");
	Construct();
	m_pModules = pModules;
}

// -----------------------------------------------------------------------------
// CATDatParser::Construct
// "Real" constructor
// -----------------------------------------------------------------------------
void CATDatParser::Construct()
{
	LOG_FUNC_ENTRY("CATDatParser::Construct");

	m_iDataVersion = 1; // Default version of data.
	m_bDllLoadFound = false;
	m_bProcessStartFound = false;
	m_bSubtestOnGoing = false;

	m_DataSaver.InitXML();
	
	m_eBuildType = -2;
	m_eProcess_state = not_started;
	m_eProjectBuildType = -1;

	m_iCurrentProcessId = 0;
	m_iLeakNumber = 0;
	m_iLogLevel = 3;
	m_iOffSet = 0;

	m_iPinPointedLeaks = 0;
	m_iPinPointedSubTestLeaks = 0;
	m_iSubtestStartHandleCount = 0;
	m_iSuccesfullRuns = 0;
	m_iTotalNumberOfLeaks = 0;
	m_iTotalRuns = 0;

	m_pRomSymbol = 0;
	m_pModules = 0;

	m_sCurrentProcessName = "";
	m_sInputFile = "";
	m_sInputFileTemp = "";
	m_sOutputFile = "";
	m_sProjectPlatform = "";
	m_vRomSymbolFiles.clear();
	m_vDllLoadModList.clear();
	m_vDllLoadModListSubTest.clear();
	m_vHandleLeaks.clear();
	m_vMemoryAddress.clear();
}

// -----------------------------------------------------------------------------
// CATDatParser::~CATDatParser
// Destructor
// -----------------------------------------------------------------------------
CATDatParser::~CATDatParser()
{
	LOG_FUNC_ENTRY("CATDatParser::~CATDatParser");

	if ( m_In.is_open() )
		m_In.close();
	// Delete temporary input file if any
	if ( !m_sInputFileTemp.empty() )
	{
		if ( FileExists( m_sInputFileTemp.c_str() ) )
			FileDelete( m_sInputFileTemp, false );
	}
	// Clean memory addresses if any
	CleanMemoryAddresses();
	// Delete rom symbol.
	if ( m_pRomSymbol )
	{
        delete m_pRomSymbol;
		m_pRomSymbol = NULL;
	}
}

// -----------------------------------------------------------------------------
// CATDatParser::Analyze
// Analyze given data file
// -----------------------------------------------------------------------------
int CATDatParser::Analyze()
{
	LOG_FUNC_ENTRY("CATDatParser::Analyze");
	// Return if input file not set
	if ( m_sInputFile.empty() )
		return AT_RETURN_CODE::INVALID_DATA_FILE;
	// If open close first
	if ( m_In.is_open() )
		m_In.close();
	// Open file
	m_In.open( m_sInputFile.c_str() );
	if ( ! m_In.good() )
		return AT_RETURN_CODE::INVALID_DATA_FILE;
	try {
		// If rom symbol file specified.
		if ( ! m_vRomSymbolFiles.empty() )
		{
			// Create new rom symbol file "parser".
			m_pRomSymbol = new CATRomSymbol();
			m_pRomSymbol->m_bShowProgressMessages = true;
			// Set symbol files.
			if ( ! m_pRomSymbol->SetSymbols( m_vRomSymbolFiles ) )
			{
				cout << AT_MSG << "Rom/Rofs symbols error: " << m_pRomSymbol->GetError() << endl;
				// If file open fails we delete it and will not use it.
				delete m_pRomSymbol;
				m_pRomSymbol = NULL;
				cout << AT_MSG << "Analyze aborted." << endl;
				return AT_RETURN_CODE::SYMBOL_FILE_ERROR;
			}
		}
		// Return code
		int iRet = 0;
		// Clear variables
		ClearParsingVariables();
		// If output defined disable printing
		if ( ! m_sOutputFile.empty() )
			m_DataSaver.SetPrintFlag( false );
		// Header
		Header();
		// Parsing
		iRet = Parse();
		// Footer
		if ( iRet == AT_RETURN_CODE::OK )
			Footer();
		// If output defined save xml
		if ( ! m_sOutputFile.empty() )
			m_DataSaver.SaveLinesToFile( m_sOutputFile.c_str(), XML_DATA );
		// Return
		return iRet;
	} catch ( int i )
	{
		cout << AT_MSG << "Error, Analyze failed. : " << i << endl;
		return AT_RETURN_CODE::UNHANDLED_EXCEPTION;
	}
}
// -----------------------------------------------------------------------------
// CATDatParser::Header
// Print header of report
// -----------------------------------------------------------------------------
void CATDatParser::Header()
{
	LOG_FUNC_ENTRY("CATDatParser::Header");
	// Analyze report header
	m_DataSaver.AddString( "Atool.exe v." );
	m_DataSaver.AddString( ATOOL_VERSION );
	m_DataSaver.AddString( "\n" );
	m_DataSaver.AddString( "Analyzing memory leaks..." );
	m_DataSaver.AddLineToLast();
}

// -----------------------------------------------------------------------------
// CATDatParser::Footer
// Print footer of report
// -----------------------------------------------------------------------------
void CATDatParser::Footer()
{
	LOG_FUNC_ENTRY("CATDatParser::Footer");
	m_DataSaver.AddString( "\nTotal Runs: " );
	m_DataSaver.AddInteger( m_iTotalRuns );
	m_DataSaver.AddLineToLast();

	int iFailedRuns = m_iTotalRuns - m_iSuccesfullRuns;
	m_DataSaver.AddString( "Failed Runs: " );
	m_DataSaver.AddInteger( iFailedRuns );
	m_DataSaver.AddLineToLast();

	char cTemp[128];
	string sResult( itoa( m_iTotalRuns, cTemp, 10 ) );
	sResult.append( ";" );
	sResult.append( itoa( iFailedRuns, cTemp, 10 ) );
	sResult.append( ";" );

	m_DataSaver.SaveXML( sResult, RESULT );
}

// -----------------------------------------------------------------------------
// CATDatParser::ClearParsingVariables
// Clear/Reset all member variables related to parsing data file
// -----------------------------------------------------------------------------
void CATDatParser::ClearParsingVariables()
{
	LOG_FUNC_ENTRY("CATDatParser::ClearParsingVariables");
	// Clear variables related to analyze
	m_eProcess_state = not_started;
	m_bProcessStartFound = false;
	m_bDllLoadFound = false;
	m_iTotalNumberOfLeaks = 0;
	m_iPinPointedLeaks = 0;
	m_iLeakNumber = 0;
	m_iTotalRuns = 0;
	m_iSuccesfullRuns = 0;
	m_bSubtestOnGoing = false;
	m_iSubtestStartHandleCount = 0;
	CleanMemoryAddresses();
}

// -----------------------------------------------------------------------------
// CATDatParser::Parse
// Parses data file. Note! header and footer of the report are done in 
// separate functions.
// -----------------------------------------------------------------------------
int CATDatParser::Parse()
{
	LOG_FUNC_ENTRY("CATDatParser::Parse");
	// Read all lines
	char cLine[MAX_LINE_LENGTH];
	do
	{
		string sLine;
		try {
			m_In.getline( cLine, MAX_LINE_LENGTH );
			sLine = cLine ;
		} catch(...)
		{
			LOG_STRING( AT_MSG << "Unexpected error, reading data file." );
			continue;
		}
		if( sLine.find( LABEL_DATA_FILE_VERSION ) != string::npos )
		{
			// Check data file version
			if(  sLine.find( AT_DATA_FILE_VERSION ) == string::npos )
			{
				return AT_RETURN_CODE::WRONG_DATA_FILE_VERSION;
			}
		}
		else if( sLine.find( LABEL_PROCESS_START ) != string::npos )
		{
			if ( ! ParseProcessStart( sLine ) )
				return AT_RETURN_CODE::ANALYZE_ERROR;
		}
		else if( sLine.find( LABEL_DLL_LOAD ) != string::npos )
		{
			if ( ! ParseDllLoad( sLine ) )
				return AT_RETURN_CODE::ANALYZE_ERROR;
		}
		else if( sLine.find( LABEL_DLL_UNLOAD ) != string::npos )
		{
			if ( ! ParseDllUnload( sLine ) )
				return AT_RETURN_CODE::ANALYZE_ERROR;
		}
		else if( sLine.find( LABEL_MEM_LEAK ) != string::npos)
		{
			if ( ! ParseMemLeak( sLine ) )
				return AT_RETURN_CODE::ANALYZE_ERROR;
		}
		else if( sLine.find( LABEL_PROCESS_END ) != string::npos )
		{
			if ( ! ParseProcessEnd( sLine ) )
				return AT_RETURN_CODE::ANALYZE_ERROR;
		}
		else if( sLine.find( LABEL_ERROR_OCCURED ) != string::npos )
		{
			if ( ! ParseErrorOccured( sLine ) )
				return AT_RETURN_CODE::ANALYZE_ERROR;
		}
		else if( sLine.find( LABEL_HANDLE_LEAK ) != string::npos )
		{
			if ( ! ParseHandleLeak( sLine ) )
				return AT_RETURN_CODE::ANALYZE_ERROR;
		}
		else if( sLine.find( LABEL_TEST_START ) != string::npos )
		{
			if ( ! ParseTestStart( sLine ) )
				return AT_RETURN_CODE::ANALYZE_ERROR;
		}
		else if( sLine.find( LABEL_TEST_END ) != string::npos )
		{
			if ( ! ParseTestEnd( sLine ) )
				return AT_RETURN_CODE::ANALYZE_ERROR;
		}
		else if( sLine.find( LABEL_LOGGING_CANCELLED ) != string::npos )
		{
			if ( ! ParseLoggingCancelled( sLine ) )
				return AT_RETURN_CODE::ANALYZE_ERROR;
		}
	}
	while( m_In.good() );
	// Message of failed run if process start was last line in data.
	if ( m_eProcess_state == ongoing )
	{
		m_DataSaver.AddString( "Test run failed.\n" );
		m_DataSaver.AddLineToLast();
	}
	return AT_RETURN_CODE::OK;
}

// -----------------------------------------------------------------------------
// CATDatParser::ParseProcessStart
// -----------------------------------------------------------------------------
bool CATDatParser::ParseProcessStart( string& sLine)
{
	LOG_FUNC_ENTRY("CATDatParser::ParseProcessStart");
	if ( m_eProcess_state == ongoing )
	{
		m_DataSaver.AddString( "Test run failed.\n" );
		m_DataSaver.AddLineToLast();
	}
	m_eProcess_state = ongoing;
	m_bProcessStartFound = true;

	// Clear handle leaks
	m_vHandleLeaks.clear();
	// Increment runs
	m_iTotalRuns++;
	// Clean leak count
	m_iTotalNumberOfLeaks = 0;
	// Clean pin pointed leaks count.
	m_iPinPointedLeaks = 0;
	// Clean leak number
	m_iLeakNumber = 0;

	// Clean loaded mods
	m_vDllLoadModList.clear();
	m_vDllLoadModListSubTest.clear();

	// Skip text PROCESS_START
	GetStringUntilNextSpace( sLine );
	// Get process name
	m_sCurrentProcessName = GetStringUntilNextSpace( sLine );
	// Get Pid
	string sPid = GetStringUntilNextSpace( sLine );
	m_iCurrentProcessId = _httoi( sPid.c_str() );

	// Header for process tart
	m_DataSaver.AddString( "\n--------------------------------\n" );
	m_DataSaver.AddString( "Test Run start (" );
	m_DataSaver.AddString( m_sCurrentProcessName.c_str() );
	m_DataSaver.AddString( "): " );

	// Get start time
	string sTime = GetStringUntilNextSpace( sLine );
	sTime = ConvertTimeToLocalTime( sTime );
	m_DataSaver.AddString( sTime.c_str() );

	// Create data for xml
	string sData( sTime );
	sData.append( ";" );
	
	// Build mode UDEB/UREL.
	string sBuildType = GetStringUntilNextSpace( sLine );

	m_DataSaver.AddString( " Build target: " );
	if( sBuildType.compare( "0" ) == 0 )
	{
		m_eBuildType = CATProject::UREL;
	}
	else if( sBuildType.compare( "1" ) == 0 )
	{
		m_eBuildType = CATProject::UDEB;
	}
	m_DataSaver.AddString( CATProject::GetBuildTypeString( m_eBuildType ).c_str() );

	// Version.
	string sVersion = GetStringUntilNextSpace( sLine );
	unsigned int iVer = 0;
	if ( hexToDec( sVersion, iVer ) && iVer != 0 )
		m_iDataVersion = iVer;

	// End line in data.
	m_DataSaver.AddLineToLast();
	
	// xml
	sData.append( CATProject::GetBuildTypeString( m_eBuildType ) );
	sData.append( ";" );
	sData.append( m_sCurrentProcessName );
	m_DataSaver.SaveXML( sData, RUN );

	// If projects platform defined check that it is same in data. (future feature).
	if ( ! m_sProjectPlatform.empty() )
	{
		// If platform info is added to data file do check here.
	}
	// If projects build type defined check that it is same in data.
	if ( m_eProjectBuildType != -1 )
	{
		if ( m_eBuildType != m_eProjectBuildType )
		{
			string sError(AT_MSG);
			sError.append( "Error, analyzed data has build type of " );
			sError.append( CATProject::GetBuildTypeString( m_eBuildType ) );
			sError.append( " and project has build type " );
			sError.append( CATProject::GetBuildTypeString( m_eProjectBuildType ) );
			sError.append( ". Pinpointed code lines are not valid." );
			m_DataSaver.AddString( sError.c_str(), false );
			m_DataSaver.AddLineToLast();
		}
	}
	return true;
}

// -----------------------------------------------------------------------------
// CATDatParser::ParseProcessEnd
// -----------------------------------------------------------------------------
bool CATDatParser::ParseProcessEnd( string& sLine )
{
	LOG_FUNC_ENTRY("CATDatParser::ParseProcessEnd");
	GetStringUntilNextSpace( sLine );

	// Get process id
	string sProcessID = GetStringUntilNextSpace( sLine );
	unsigned long iProcessID = _httoi( sProcessID.c_str() );

	// Get time
	string sTime = GetStringUntilNextSpace( sLine );

	// Convert leak time
	sTime = ConvertTimeToLocalTime( sTime );

	// Process started?
	if( iProcessID == m_iCurrentProcessId )
	{
		m_iSuccesfullRuns++;
		m_DataSaver.AddLineToLast();
		m_DataSaver.AddString( "Test Run end (" );
		m_DataSaver.AddString( m_sCurrentProcessName.c_str() );
		m_DataSaver.AddString( "): " );
		m_DataSaver.AddString( sTime.c_str() );
		m_DataSaver.AddLineToLast();
		m_DataSaver.AddString( "Build target: " );
		m_DataSaver.AddString( CATProject::GetBuildTypeString( m_eBuildType ).c_str() );
		m_DataSaver.AddLineToLast();

		m_eProcess_state = stopped;
		// Number of leaks
		if ( m_iLogLevel == 1 || m_iLogLevel == 2 )
		{
			if ( m_iPinPointedLeaks > 0 )
			{
				m_DataSaver.AddInteger( m_iPinPointedLeaks );
				m_DataSaver.AddString( " number of pinpointed memory leak(s)." );
				m_DataSaver.AddLineToLast();
			}
			m_DataSaver.AddInteger( m_iLeakNumber );
			m_DataSaver.AddString( " total number of memory leak(s)." );
			m_DataSaver.AddLineToLast();
		}
		else
		{
			m_DataSaver.AddInteger( m_iTotalNumberOfLeaks );
			m_DataSaver.AddString( " memory leak(s) found." );
			m_DataSaver.AddLineToLast();
		}
		
		// xml
		char cTemp[128];
		m_DataSaver.SaveXML( itoa( m_iTotalNumberOfLeaks, cTemp, 10 ) , MEM_LEAKS );

		// Print all modules which have leaks
		for( size_t i = 0 ; i < m_vDllLoadModList.size() ; i++ )
		{
			if( m_vDllLoadModList.at(i).iLeaks > 0 )
			{
				m_DataSaver.AddInteger( m_vDllLoadModList.at(i).iLeaks );
				m_DataSaver.AddString( " memory leak(s) in module: " );
				m_DataSaver.AddString( m_vDllLoadModList.at(i).sModuleName.c_str() );
				m_DataSaver.AddLineToLast();

				// xml
				string sModuleNameAndLeaks( m_vDllLoadModList[i].sModuleName );
				sModuleNameAndLeaks.append(";");
				sModuleNameAndLeaks.append( itoa( m_vDllLoadModList[i].iLeaks, cTemp, 10 ) );
				m_DataSaver.SaveXML( sModuleNameAndLeaks , MEM_LEAK_MODULE );
			}
		}
		
		if ( m_vHandleLeaks.size() > 0 )
		{
			// We have handle leaks
			bool bHandLeaksFound = false;
			int iTotalNrOfLeaks = 0;
			// Print handle leaks
			for( size_t i = 0 ; i < m_vHandleLeaks.size() ; i++ )
			{
				string sTempHandleLeak( m_vHandleLeaks[i] );
				// Name.
				string sHandleLeakModule( GetStringUntilNextSpace( sTempHandleLeak ) );
				// Count.
				string sNrOfLeaks( GetStringUntilNextSpace(sTempHandleLeak) );
				unsigned long iNrOfLeaks = _httoi( sNrOfLeaks.c_str() );
				iTotalNrOfLeaks += iNrOfLeaks;
				if( iNrOfLeaks )
				{
					if( !bHandLeaksFound )
					{
						m_DataSaver.SaveXML( sNrOfLeaks , HANDLE_LEAKS );
					}
					bHandLeaksFound = true;
					m_DataSaver.AddInteger( iNrOfLeaks );
					// Just print out how many leaks found.
					// Because its always unknown.
					m_DataSaver.AddString( " handle leak(s) found." );
					m_DataSaver.AddLineToLast();

					// xml
					string sXMLInfo( sHandleLeakModule );
					sXMLInfo.append( ";" ); sXMLInfo.append( sNrOfLeaks );
					m_DataSaver.SaveXML( sXMLInfo , HANDLE_LEAK_MODULE );
				}
			}
			// Update number if handle leaks
			m_DataSaver.SaveXML( itoa( iTotalNrOfLeaks, cTemp, 10 ) , HANDLE_LEAKS );
			if( !bHandLeaksFound )
			{
				//m_DataSaver.AddLineToLast();
				m_DataSaver.AddString( TEXT_NO_HANDLE_LEAKS );
				m_DataSaver.AddLineToLast();
			}
		}
		else
		{
			// No handle leaks
			m_DataSaver.AddLineToLast();
			m_DataSaver.AddString( TEXT_NO_HANDLE_LEAKS );
			m_DataSaver.AddLineToLast();
		}

		// Process end to xml
		m_DataSaver.SaveXML( sTime, RUN_END );
		// Reset current process
		m_iCurrentProcessId = 0;
	}
	
	// If no dll load or process start found
	if ( ! m_bProcessStartFound || !m_bDllLoadFound )
	{
		m_DataSaver.AddLineToLast();
		m_DataSaver.AddString( AT_ANALYZE_INSUFFICIENT_LOGGING_DATA );
		m_DataSaver.AddLineToLast();
	}
	
	return true;
}

// -----------------------------------------------------------------------------
// CATDatParser::ParseDllLoad
// -----------------------------------------------------------------------------
bool CATDatParser::ParseDllLoad( string& sLine )
{
	LOG_FUNC_ENTRY("CATDatParser::ParseDllLoad");
	//DLL_LOAD <DLL name> <Time stamp> <Memory start address> <Memory end address>
	m_bDllLoadFound = true;
	DLL_LOAD_INFO structDllInfo;
	structDllInfo.iStartAddress = 0;
	structDllInfo.iEndAddress = 0;
	structDllInfo.iLeaks = 0;

	// Skip "DLL_LOAD "
	GetStringUntilNextSpace( sLine );

	// Get module name
	structDllInfo.sModuleName = GetStringUntilNextSpace( sLine );
	ChangeToLower( structDllInfo.sModuleName );

	// Create module from this if project platform emulator
	if ( _stricmp( "winscw", m_sProjectPlatform.c_str() ) == 0 )
		CreateWinscwModule( structDllInfo.sModuleName );

	if ( m_iDataVersion >= AT_DLL_TIMESTAMP_DATA_VERSION )
	{
		// Pickup module loading time.
		string sLoadTime = GetStringUntilNextSpace( sLine );
		unsigned long long ull;
		if ( hexToDec( sLoadTime, ull ) )
			structDllInfo.iLoadTime = ull;
	}

	// Get dll start memory string address from line
	// Convert string address to real memory address
	structDllInfo.iStartAddress = 
		_httoi( GetStringUntilNextSpace( sLine ).c_str() );

	// Get dll end memory string address from line
	// Convert string address to real memory address
	structDllInfo.iEndAddress = 
		_httoi( 
		GetStringUntilNextSpace( sLine ).c_str() );

	// Is module already loaded, if not add it to list.
	bool bFound = false;
	for( vector<DLL_LOAD_INFO>::iterator it = m_vDllLoadModList.begin();
		it != m_vDllLoadModList.end() ; it++ )
	{
		if( (*it).sModuleName.compare( structDllInfo.sModuleName ) == 0 )
		{
			bFound = true;
			break;
		}
	}
	if( ! bFound )
		m_vDllLoadModList.push_back( structDllInfo );

	// Sub test module list.
	bFound = false;
	for( vector<DLL_LOAD_INFO>::iterator it = m_vDllLoadModListSubTest.begin();
		it != m_vDllLoadModListSubTest.end() ; it++ )
	{
		if( (*it).sModuleName.compare( structDllInfo.sModuleName ) == 0 )
		{
			bFound = true;
			break;
		}
	}
	if( ! bFound )
		m_vDllLoadModListSubTest.push_back( structDllInfo );

	return true;
}

// -----------------------------------------------------------------------------
// CATDatParser::ParseDllUnload
// -----------------------------------------------------------------------------
bool CATDatParser::ParseDllUnload( string& sLine )
{
	LOG_FUNC_ENTRY("CATDatParser::ParseDllUnload");

	// Ignore unloads on older version because no timestamps.
	if ( m_iDataVersion < AT_DLL_TIMESTAMP_DATA_VERSION )
	{
		return true;
	}

	// Skip "DLL_UNLOAD "
	GetStringUntilNextSpace( sLine );

	// Get module name
	string sModuleName = GetStringUntilNextSpace( sLine );
	ChangeToLower( sModuleName );

	// Unload time
	unsigned long long ull;
	string sUnload = GetStringUntilNextSpace( sLine );
	if ( ! hexToDec( sUnload, ull ) )
		return true;

	// Set module unload time.
	vector<DLL_LOAD_INFO>::iterator it;
	for( it = m_vDllLoadModList.begin() ; it != m_vDllLoadModList.end() ; it++ )
	{
		if ( sModuleName.compare( it->sModuleName ) == 0 )
		{
			(*it).iUnloadTime = ull;
			break;
		}
	}
	for( it = m_vDllLoadModListSubTest.begin() ; it != m_vDllLoadModListSubTest.end() ; it++ )
	{
		if ( sModuleName.compare( it->sModuleName ) == 0 )
		{
			(*it).iUnloadTime = ull;
			break;
		}
	}
	return true;
}
// -----------------------------------------------------------------------------
// CATDatParser::ParseLoggingCancelled
// -----------------------------------------------------------------------------
bool CATDatParser::ParseLoggingCancelled( string& sLine )
{
	LOG_FUNC_ENTRY("CATDatParser::ParseLoggingCancelled");
	// Skip text "LOGGING_CANCELLED"
	GetStringUntilNextSpace( sLine );

	// Get time
	string sTime( GetStringUntilNextSpace( sLine ) );
	sTime = ConvertTimeToLocalTime( sTime );
	m_DataSaver.AddString( "Logging Cancelled." );
	m_DataSaver.AddLineToLast();
	return true;
}

// -----------------------------------------------------------------------------
// CATDatParser::ParseHandleLeak
// -----------------------------------------------------------------------------
bool CATDatParser::ParseHandleLeak( string& sLine )
{
	LOG_FUNC_ENTRY("CATDatParser::ParseHandleLeak");
	// Skip text "HANDLE_LEAK"
	GetStringUntilNextSpace( sLine );
	m_vHandleLeaks.push_back( sLine );
	return true;
}

// -----------------------------------------------------------------------------
// CATDatParser::ParseTestStart
// -----------------------------------------------------------------------------
bool CATDatParser::ParseTestStart( string& sLine )
{
	LOG_FUNC_ENTRY("CATDatParser::ParseTestStart");
	m_bSubtestOnGoing = true;
	m_iLeakNumber = 0;
	m_iPinPointedSubTestLeaks = 0;

	// Reset subtest leaked modules list
	for( size_t i = 0 ; i < m_vDllLoadModListSubTest.size() ; i++ )
	{
		m_vDllLoadModListSubTest.at(i).iLeaks = 0;
	}

	// Skip text "TEST_START"
	GetStringUntilNextSpace( sLine );
	// Time
	string sTime( GetStringUntilNextSpace( sLine ) );
	sTime = ConvertTimeToLocalTime( sTime );
	// Name
	string sSubTestName( GetStringUntilNextSpace( sLine ) );				
	m_DataSaver.AddLineToLast();

	// Get handle count in subtest start
	string sSubTestStartHandleCount( GetStringUntilNextSpace( sLine ) );
	m_iSubtestStartHandleCount = atoi( sSubTestStartHandleCount.c_str() );

	// Add start to report
	m_DataSaver.AddString( "\nSub test (" );
	m_DataSaver.AddString( sSubTestName.c_str() );
	m_DataSaver.AddString( ") start: " );
	m_DataSaver.AddString( sTime.c_str() );

	// m_DataSaver.AddLineToLast();

	// Add start to xml
	string sResult( sSubTestName );
	sResult.append( ";" );
	sResult.append( sTime );
	sResult.append( ";" );
	m_DataSaver.SaveXML( sResult, TEST_START );
	return true;
}

// -----------------------------------------------------------------------------
// CATDatParser::ParseTestEnd
// -----------------------------------------------------------------------------
bool CATDatParser::ParseTestEnd( string& sLine )
{
	LOG_FUNC_ENTRY("CATDatParser::ParseTestEnd");
	// Skip text "TEST_END"
	GetStringUntilNextSpace( sLine );

	// Time
	string sTime( GetStringUntilNextSpace( sLine ) );
	sTime = ConvertTimeToLocalTime( sTime );

	// Name
	string sSubTestName( GetStringUntilNextSpace( sLine ) );
	m_DataSaver.AddLineToLast();

	// Add test end info to report
	m_DataSaver.AddString( "Sub test (" );
	m_DataSaver.AddString( sSubTestName.c_str() );
	m_DataSaver.AddString( ") end: " );
	m_DataSaver.AddString( sTime.c_str() );
	m_DataSaver.AddLineToLast();

	// Leak count to report in subtest
	if( m_iLeakNumber > 0 )
	{
		if ( m_iLogLevel == 1 || m_iLogLevel == 2 )
		{
			m_DataSaver.AddInteger( m_iPinPointedSubTestLeaks );
			m_DataSaver.AddString( " number of pinpointed memory leaks." );
			m_DataSaver.AddLineToLast();
			m_DataSaver.AddInteger( m_iLeakNumber );
			m_DataSaver.AddString( " memory leaks found." );
		}
		else
		{
			m_DataSaver.AddInteger( m_iLeakNumber );
			m_DataSaver.AddString( " memory leaks found." );
		}
	}
	else
	{
		m_DataSaver.AddString( "No memory leaks found." );
	}
	m_DataSaver.AddLineToLast();

	// Use sTime to store info to xml
	sTime.append(";");
	char cTemp[128];
	// Print all modules whitch have leaks
	for( unsigned int i = 0 ; i < m_vDllLoadModListSubTest.size() ; i++ )
	{
		if( m_vDllLoadModListSubTest.at(i).iLeaks > 0 )
		{
			// Normal report
			m_DataSaver.AddInteger( m_vDllLoadModListSubTest[i].iLeaks );
			m_DataSaver.AddString( " memory leaks in module: " );
			m_DataSaver.AddString( m_vDllLoadModListSubTest.at(i).sModuleName.c_str() );
			m_DataSaver.AddLineToLast();
			// xml
			string sModuleNameAndLeaks( m_vDllLoadModListSubTest.at(i).sModuleName );
			sModuleNameAndLeaks.append(";");
			sModuleNameAndLeaks.append( itoa( m_vDllLoadModListSubTest.at(i).iLeaks, cTemp, 10 ) );
			m_DataSaver.SaveXML( sModuleNameAndLeaks , SUBTEST_MEM_LEAK_MODULE );
		}
	}
	// Handle count
	int iEndHandleCount = atoi( GetStringUntilNextSpace( sLine ).c_str() );
	// Is there handle leaks in subtest?
	if( iEndHandleCount > m_iSubtestStartHandleCount )
	{
		// Print normal report
		m_DataSaver.AddInteger( iEndHandleCount - m_iSubtestStartHandleCount );
		m_DataSaver.AddString( " handle leaks in subtest: " );
		m_DataSaver.AddString( sSubTestName.c_str() );
		m_DataSaver.AddString( "." );
		m_DataSaver.AddLineToLast();

		// Print handle leaks to XML
		string sNrOfHandleLeaks( itoa( iEndHandleCount - m_iSubtestStartHandleCount, cTemp, 10 ) );
		sNrOfHandleLeaks.append( ";" );
		m_DataSaver.SaveXML( sNrOfHandleLeaks, SUBTEST_HANDLE_LEAKS );
	}
	else
	{
		// No handle leaks
		m_DataSaver.AddString( TEXT_NO_HANDLE_LEAKS );
		m_DataSaver.AddLineToLast();
	}
	// Save xml
	m_DataSaver.SaveXML( sTime, TEST_END );
	// Back to normal leaks
	m_bSubtestOnGoing = false;
	return true;
}

// -----------------------------------------------------------------------------
// CATDatParser::ParseErrorOccured
// -----------------------------------------------------------------------------
bool CATDatParser::ParseErrorOccured( string& sLine )
{
	LOG_FUNC_ENTRY("CATDatParser::ParseErrorOccured");
	string sTime,sError;

	// Skip text "ERROR_OCCURED:"
	GetStringUntilNextSpace( sLine );

	// Get error
	sError = GetStringUntilNextSpace( sLine );
	// Get and convert error time
	sTime = GetStringUntilNextSpace( sLine );
	sTime = ConvertTimeToLocalTime( sTime );

	// Print error line
	m_DataSaver.AddLineToLast();
	m_DataSaver.AddString( "Error occured on: " );
	m_DataSaver.AddString( sTime.c_str() );
	m_DataSaver.AddString( ". " );
	m_DataSaver.AddString( "Symbian error code: " );
	m_DataSaver.AddString( sError.c_str() );
	m_DataSaver.AddString( "." );
	m_DataSaver.AddLineToLast();

	return true;
}

// -----------------------------------------------------------------------------
// CATDatParser::ParseMemLeak
// -----------------------------------------------------------------------------
bool CATDatParser::ParseMemLeak( string& sLine )
{
	LOG_FUNC_ENTRY("CATDatParser::ParseMemLeak");
	// Increment leak count
	if ( ! m_bSubtestOnGoing )
		m_iTotalNumberOfLeaks++;

	// Increase leak number
	m_iLeakNumber++;

	// Leak data variables
	string sModuleName;
	string sLeakSize;
	string sTime;
	unsigned long long iTime = 0;
	string sLeakAddress;
	
	// Skip text "MEM_LEAK"
	GetStringUntilNextSpace( sLine );
	// Get leak address
	sLeakAddress = GetStringUntilNextSpace( sLine );
	// Get time
	sTime = GetStringUntilNextSpace( sLine );
	// Convert time to decimal
	hexToDec( sTime, iTime );
	// Get memory reserve size
	sLeakSize = GetStringUntilNextSpace( sLine );
	// Convert leak time
	sTime = ConvertTimeToLocalTime( sTime );

	// Loop thru call stack and put memory addresses in vector
	CleanMemoryAddresses(); // Clean memory address vector
	CATMemoryAddress* pMemAddr = 0;
	vector<string> vStrings = ParseStringToVector( sLine, ' ' );
	for( size_t i = 0; i < vStrings.size() ; i++ )
	{
		pMemAddr = new CATMemoryAddress( vStrings.at(i), m_iOffSet );
		// Set address time
		pMemAddr->SetTime( iTime );
		// Set address module name
		if ( pMemAddr->FindSetModuleName( &m_vDllLoadModList ) )
		{
			// Increment leaks in module once
			if ( sModuleName.empty() )
			{
				if ( m_bSubtestOnGoing )
					m_vDllLoadModListSubTest.at( pMemAddr->GetDllLoadInfoIndex() ).iLeaks++;
				else
					m_vDllLoadModList.at( pMemAddr->GetDllLoadInfoIndex() ).iLeaks++;
				// Set leak's module where it was located.
				sModuleName = pMemAddr->GetModuleName();
			}
		}
		// Add it to vector
		m_vMemoryAddress.push_back( pMemAddr );
	}
	// If logging level is 0 Skip printing / locating code lines for call stack items.
	if ( m_iLogLevel == 0 )
		return true;
	if ( m_pModules && vStrings.size() > 0 )
	{
		// Have we successfully located code line for memory address
		bool bSuccesfullAddressToLine = false;
		for( size_t x = 0; x < m_vMemoryAddress.size(); x++ )
		{
			int iIndexInDll = m_vMemoryAddress.at( x )->GetDllLoadInfoIndex();
			if ( iIndexInDll != -1 )
			{
				// Dll module name from data file
				string sDllName = m_vDllLoadModList.at( iIndexInDll ).sModuleName;
				// Find module from project. These are read from makefiles.
				for ( size_t y = 0; y < m_pModules->size() ; y++ )
				{
					// Module name from project data (makefiles)
					string sModuleName = m_pModules->at( y )->GetBinaryName();
					// If we find module from project ones, use it to located code line for memory address
					// Note! dll names can be pretty messy i.e. DLL_LOAD 10281fc6.dll{000a0000}[10281fc6] 81d57b88 81e60a90
					if ( sDllName.find( sModuleName ) != string::npos )
					{
						m_pModules->at( y )->AddressToLine( m_vMemoryAddress.at( x ) );
						if ( ! bSuccesfullAddressToLine )
						{
							int iPPState = m_vMemoryAddress.at( x )->GetAddressToLineState();
							if ( iPPState == CATMemoryAddress::EXACT || iPPState == CATMemoryAddress::FUNCTION )
							{
								bSuccesfullAddressToLine = true;
								if ( m_bSubtestOnGoing )
									m_iPinPointedSubTestLeaks++;
								else
									m_iPinPointedLeaks++;
							}
						}
					}
				}
			}
		}
		// If rom/rofs specified we use it to try get binary and function names
		// for addresses currently out of process range.
		if ( m_pRomSymbol )
		{
			for( size_t x = 0; x < m_vMemoryAddress.size(); x++ )
			{
				if ( m_vMemoryAddress.at(x)->GetAddressToLineState() == CATMemoryAddress::OUT_OF_PROCESS
					|| m_vMemoryAddress.at(x)->GetAddressToLineState() == CATMemoryAddress::OUT_OF_RANGE )
				{
					m_pRomSymbol->AddressToLine( m_vMemoryAddress.at(x) );
				}
			}
		}
	}
	// Print leak
	PrintMemLeak( sTime, sLeakSize, sLeakAddress, sModuleName);
	return true;
}

// -----------------------------------------------------------------------------
// CATDatParser::PrintMemLeak
// -----------------------------------------------------------------------------
void CATDatParser::PrintMemLeak(const string& sTime,
							   const string& sLeakSize,
							   const string& sLeakAddr,
							   const string& sModuleName)
{
	LOG_FUNC_ENTRY("CATDatParser::PrintMemLeak");
	// Print header data of leak
	m_DataSaver.AddString("\nMemory leak ");
	m_DataSaver.AddInteger( m_iLeakNumber, true);

	// Leak size
	m_DataSaver.AddString( " (" );
	m_DataSaver.AddInteger( _httoi( sLeakSize.c_str() ), true );
	m_DataSaver.AddString( " bytes) " );

	// Leak address
	m_DataSaver.AddString("(0x");
	m_DataSaver.AddString( sLeakAddr.c_str(), true );
	m_DataSaver.AddString( ") " );

	// Time
	m_DataSaver.AddString( sTime.c_str(), true );
	m_DataSaver.AddString( " " );
	
	// Module name
	m_DataSaver.AddString( sModuleName.c_str(), true );
	m_DataSaver.SaveCarbideDataHeader();
	
	// Add header line
	m_DataSaver.AddLineToLast();

	// Print the call stack items
	for( size_t i = 0 ; i < m_vMemoryAddress.size() ; i++ )
	{
		// On log levels 1 & 2 we only print located code lines.
		#ifndef ADDR2LINE
		if( 
			( m_iLogLevel == 1 || m_iLogLevel == 2 )
			&&
			( m_vMemoryAddress.at(i)->GetAddressToLineState() != CATMemoryAddress::EXACT
			&& m_vMemoryAddress.at(i)->GetAddressToLineState() != CATMemoryAddress::FUNCTION )
			)
		{
			// Skips to next
			continue;
		}
		#endif
		#ifdef ADDR2LINE
		if( ( m_iLogLevel == 1 || m_iLogLevel == 2 )
			&& m_vMemoryAddress.at(i)->GetAddressToLineState() != CATMemoryAddress::EXACT )
		{
			// Skips to next
			continue;
		}
		#endif
		else if ( m_vMemoryAddress.at(i)->GetAddressToLineState() == CATMemoryAddress::OUT_OF_PROCESS )
		{
			// Is memory address out of modules range
			string sTemp;
			sTemp.append( m_vMemoryAddress.at(i)->GetAddressString() );
			sTemp.append( " Address out of process memory.");
			m_DataSaver.AddString( sTemp.c_str(), true );
			m_DataSaver.AddLineToLast();
			continue;
		}
		
		// Print memory address
		m_DataSaver.AddString( m_vMemoryAddress.at(i)->GetAddressString().c_str(), true );

		// Space (only for console output)
		m_DataSaver.AddString( " " );

		m_DataSaver.AddCarbideData( 
			NumberToHexString( m_vMemoryAddress.at(i)->GetOffSetFromModuleStart() ) );

		// Module name
		m_DataSaver.AddString( m_vMemoryAddress.at(i)->GetModuleName().c_str(), true );

		// Print call stack memory address details depending on state of memory address
		switch( m_vMemoryAddress.at(i)->GetAddressToLineState() )
		{
			// Address outside of known processes
		case CATMemoryAddress::OUT_OF_PROCESS:
			m_DataSaver.AddLineToLast();
			break;
			// Address located outside of known modules symbols
		case CATMemoryAddress::OUT_OF_RANGE:
			m_DataSaver.AddString( " " );
			m_DataSaver.AddString( "???", true );
			m_DataSaver.AddLineToLast();
			break;
		// Symbol state is currently used when using rom symbol file.
		// From it we get module name & function name.
		case CATMemoryAddress::SYMBOL:
			m_DataSaver.AddString( " " );
			m_DataSaver.AddString( m_vMemoryAddress.at(i)->GetFunctionName().c_str(), true );
			if ( ! m_vMemoryAddress.at( i )->GetFileName().empty() )
			{
				m_DataSaver.AddString( " (" );
				m_DataSaver.AddString( m_vMemoryAddress.at(i)->GetFileName().c_str(), true );
				m_DataSaver.AddString( ")" );
			}
			m_DataSaver.AddLineToLast();
			break;
		// Lst & Map implementation
		#ifndef ADDR2LINE
		case CATMemoryAddress::FUNCTION:
		case CATMemoryAddress::EXACT:
			m_DataSaver.AddString( " " );
			m_DataSaver.AddString( m_vMemoryAddress.at(i)->GetFunctionName().c_str(), true );
			// Small difference displaying details depending on build urel/udeb
			if ( m_eBuildType == CATProject::UREL )
			{
				// UREL
				// Set build info to data saver
				m_DataSaver.SetBuild( false );
				// urel = functionname: linenumber (filename)
				m_DataSaver.AddString( ": " );
				if (  m_vMemoryAddress.at(i)->GetFunctionLineNumber() != -1 )
					m_DataSaver.AddInteger( m_vMemoryAddress.at(i)->GetFunctionLineNumber(), true );
				else if (  m_vMemoryAddress.at(i)->GetExactLineNumber() != -1 )
					m_DataSaver.AddInteger( m_vMemoryAddress.at(i)->GetExactLineNumber(), true );
				m_DataSaver.AddString( " (" );
				m_DataSaver.AddString( m_vMemoryAddress.at(i)->GetFileName().c_str(), true );
				m_DataSaver.AddString( ")" );
				m_DataSaver.AddLineToLast();
			}
			else
			{
				// UDEB
				// udeb = functionname: (filename:linenumber)
				m_DataSaver.AddString( " (" );
				m_DataSaver.AddString( m_vMemoryAddress.at(i)->GetFileName().c_str(), true );
				m_DataSaver.AddString( ":" );
				if(  m_vMemoryAddress.at(i)->GetExactLineNumber() != -1 )
					m_DataSaver.AddInteger( m_vMemoryAddress.at(i)->GetExactLineNumber(), true );
				else
					m_DataSaver.AddString( "???", true );
				m_DataSaver.AddString( ")" );
				m_DataSaver.AddLineToLast();
			}
			break;
		#endif
		// addr2line implementation (new).
		#ifdef ADDR2LINE
		case CATMemoryAddress::FUNCTION:
			m_DataSaver.AddString( " " );
			m_DataSaver.AddString( m_vMemoryAddress.at(i)->GetFunctionName().c_str(), true );
			m_DataSaver.AddString( " (" );
			m_DataSaver.AddString( m_vMemoryAddress.at(i)->GetFileName().c_str(), true );
			m_DataSaver.AddString( ":" );
			if(  m_vMemoryAddress.at(i)->GetExactLineNumber() != -1 )
				m_DataSaver.AddInteger( m_vMemoryAddress.at(i)->GetFunctionLineNumber(), true );
			else
				m_DataSaver.AddString( "???", true );
			m_DataSaver.AddString( ")" );
			m_DataSaver.AddLineToLast();
			break;
		case CATMemoryAddress::EXACT:
			m_DataSaver.AddString( " " );
			m_DataSaver.AddString( m_vMemoryAddress.at(i)->GetFunctionName().c_str(), true );
			m_DataSaver.AddString( " (" );
			m_DataSaver.AddString( m_vMemoryAddress.at(i)->GetFileName().c_str(), true );
			m_DataSaver.AddString( ":" );
			if(  m_vMemoryAddress.at(i)->GetExactLineNumber() != -1 )
				m_DataSaver.AddInteger( m_vMemoryAddress.at(i)->GetExactLineNumber(), true );
			else
				m_DataSaver.AddString( "???", true );
			m_DataSaver.AddString( ")" );
			m_DataSaver.AddLineToLast();
			break;
		#endif
		} // End switch
		// On logging level 1 we only print one located code line
		#ifndef ADDR2LINE
		if ( m_iLogLevel == 1 && ( m_vMemoryAddress.at(i)->GetAddressToLineState() == CATMemoryAddress::EXACT ||
			m_vMemoryAddress.at(i)->GetAddressToLineState() == CATMemoryAddress::FUNCTION ) )
			break;
		#endif
		#ifdef ADDR2LINE
		if ( m_iLogLevel == 1 && m_vMemoryAddress.at(i)->GetAddressToLineState() == CATMemoryAddress::EXACT )
			break;
		#endif
	} // End call stack items loop
}

// -----------------------------------------------------------------------------
// CATDatParser::SetInputFile
// -----------------------------------------------------------------------------
void CATDatParser::SetInputFile(const string& sInputFile)
{
	LOG_FUNC_ENTRY("CATDatParser::SetInputFile");
	m_sInputFile = sInputFile;
}

// -----------------------------------------------------------------------------
// CATDatParser::SetOutputFile
// -----------------------------------------------------------------------------
void CATDatParser::SetOutputFile(const string& sOutpuFile)
{
	LOG_FUNC_ENTRY("CATDatParser::SetOutputFile");
	m_sOutputFile = sOutpuFile;
}

// -----------------------------------------------------------------------------
// CATDatParser::SetRomSymbolFiles
// -----------------------------------------------------------------------------
void CATDatParser::SetRomSymbolFiles(const vector<string>& vRomSymbolFiles)
{
	LOG_FUNC_ENTRY("CATDatParser::SetRomSymbolFiles");
	m_vRomSymbolFiles = vRomSymbolFiles;
}

// -----------------------------------------------------------------------------
// CATDatParser::SetLogLevel
// -----------------------------------------------------------------------------
void CATDatParser::SetLogLevel(int iLogLevel)
{
	LOG_FUNC_ENTRY("CATDatParser::SetLogLevel");
	m_iLogLevel = iLogLevel;
}

// -----------------------------------------------------------------------------
// CATDatParser::GetLogLevel
// -----------------------------------------------------------------------------
int CATDatParser::GetLogLevel() const
{
	LOG_LOW_FUNC_ENTRY("CATDatParser::GetLogLevel");
	return m_iLogLevel;
}

// -----------------------------------------------------------------------------
// CATDatParser::CleanMemoryAddresses
// -----------------------------------------------------------------------------
void CATDatParser::CleanMemoryAddresses()
{
	LOG_LOW_FUNC_ENTRY("CATDatParser::CleanMemoryAddresses");
	// Cleanup memory addressses.
	for( vector<CATMemoryAddress*>::iterator it = m_vMemoryAddress.begin(); it != m_vMemoryAddress.end(); it++ )
	{
		delete *it;
	}
	m_vMemoryAddress.clear();
}

// -----------------------------------------------------------------------------
// CATDatParser::ConvertTimeToLocalTime
// -----------------------------------------------------------------------------
string CATDatParser::ConvertTimeToLocalTime( string sInputTime )
{
	LOG_LOW_FUNC_ENTRY("CATDatParser::ConvertTimeToLocalTime");
	//Is process end abnormal?
	if( sInputTime.compare( LABEL_ABNORMAL ) == 0 )
	{
		return string( AT_ANALYZE_ABNORMAL_EXIT );
	}
	else
	// Check that input time is at least 32-bit
	if( sInputTime.length() <= 8 )
	{
		sInputTime.clear();
		return sInputTime;
	}

	string sTemp = sInputTime;
	const char* pTemp = sTemp.c_str();

	// Are all characters hex
	for( unsigned int i = 0 ; i < sTemp.size() ; i++ )
	{
		if( !IsHexCharacter( (pTemp + i) ) )
		{
			return sInputTime;
		}
	}
	
	// Get LSB bits
	string sLsb;
	sLsb.append( sInputTime.substr( sInputTime.length()-8, sInputTime.length() ) );
	unsigned int iLsbTime = (unsigned int)_httoi( sLsb.c_str() );

	// Get MSB bits
    string sMsb;
	sMsb.append( sInputTime.substr( 0, sInputTime.length()-8 ) );
	unsigned int iMsbTime = (unsigned int)_httoi( sMsb.c_str() );

	// Get time in microsecods
	long long sdf = iMsbTime * 0x100000000 + iLsbTime;

	// Get original time (starting at year 1970 )
	long long llOrigTime = sdf;

	// Get seconds
	sdf = ( sdf )/1000000;
		
	// Check that sdf contains some time value
	if( sdf <= 0)
	{
		// Error in time calculation
		// Return empty string
		sInputTime.clear();
		return sInputTime;
	}

	// Original time after year 1970 in seconds
	long long llOrignTimeInSeconds = sdf;
	
	// Calculate new time which does not include millisecods
	long long llDiffTime = (llOrignTimeInSeconds * 1000000);

	// Calculate time difference in milliseconds
	int llDiffTimeInMilliSecods = (int)( llOrigTime - llDiffTime )/1000;
	
	// Convert difference time to char
	char cDiffInMilliSeconds[20];
    _itoa( llDiffTimeInMilliSecods, cDiffInMilliSeconds, 10 );

	// Time info structure
	struct tm *timeinfo;

	// Get local time
	timeinfo = localtime ( (time_t*) &sdf );

	// Create string and append memory leak time to it
	string sTime;
	sTime.append( asctime( timeinfo ) );

	// Remove last char of locale time string which is \n
	sTime.resize( (int)sTime.length()-1 );
	
	// Get last space index
	int iLastSpace = (int)sTime.find_last_of(" ");

	// If last space index is valid
	if( iLastSpace <= (int)sTime.length() && iLastSpace > 0)
	{
		string sTempTime;
		// Append first part of original time string
		sTempTime.append( sTime.substr( 0, iLastSpace ) );
		
		// Append millisecods
		sTempTime.append( "." );
		sTempTime.append( cDiffInMilliSeconds );

		// Append the rest of the original time string part
		sTempTime.append( sTime.substr( iLastSpace, sTime.length()));

		// Clear original and append new time string which includes millisecods
		sTime.clear();
		sTime.append( sTempTime );
	}

	// Return memory leak time
	return sTime.c_str();
}

bool CATDatParser::CreateWinscwModule( const string& sBinaryName )
{
	LOG_FUNC_ENTRY( "CATDatParser::CreateWinscwModule" );
	// Is module already created?
	for( vector<CATModule2*>::iterator it = m_pModules->begin(); it != m_pModules->end(); it++ )
	{
		if ( _stricmp( sBinaryName.c_str(), (*it)->GetBinaryName().c_str() ) == 0 )
			return true;
	}
	// No create new one and set its values.
	CATModule2* mod = new CATModule2();
	mod->SetTarget( RemovePathAndExt( sBinaryName, true ) );
	mod->SetRequestedTargetExt( GetExtension( sBinaryName ) );
	mod->SetReleasePath( string( "\\epoc32\\release" ) );
	if ( m_eBuildType == CATProject::UDEB )
		mod->SetFullVariantPath( string( "winscw\\udeb" ) );
	else
		mod->SetFullVariantPath( string( "winscw\\urel" ) );
	mod->SetVariantPlatform( string( "winscw" ) );
	m_pModules->push_back( mod );
	return true;
}

// -----------------------------------------------------------------------------
// CATDatParser::FindModuleUsingAddress
// Function finds module using given address.
// -----------------------------------------------------------------------------
int CATDatParser::FindModuleUsingAddress( unsigned long iAddress ) const
{
	LOG_LOW_FUNC_ENTRY("CATDatParser::FindModuleUsingAddress");
	int iRet = -1;
	for( unsigned int i = 0 ; i < m_vDllLoadModList.size() ; i++ )
	{
		// Is address between start and end address?
		if( iAddress > m_vDllLoadModList[i].iStartAddress && iAddress < m_vDllLoadModList[i].iEndAddress )
		{
			iRet = i;
			break;
		}	
	}
	return iRet;
}

// -----------------------------------------------------------------------------
// CATDatParser::FindModuleUsingPID
// Function finds module using module id.
// -----------------------------------------------------------------------------
/*
int CATDatParser::FindModuleUsingPID( unsigned long iPID ) const
{
	LOG_FUNC_ENTRY("CATDatParser::FindModuleUsingPID");

	int iRet = -1;

	// Change module name characters to lowercase
	for( unsigned int i = 0 ; i < m_vDllLoadModList.size() ; i++ )
	{
		if( m_vDllLoadModList[i].iPID == iPID )
		{
			iRet = i;
			break;
		}	
	}
	return iRet;
}
*/
// -----------------------------------------------------------------------------
// CATDatParser::FindModuleUsingName
// Function finds module using module name.
// -----------------------------------------------------------------------------
int CATDatParser::FindModuleUsingName( const char* pModName )
{
	LOG_LOW_FUNC_ENTRY("CATDatParser::FindModuleUsingName");

	// Mod name empty?
	if( pModName == NULL || *pModName == 0 )
		return -1;

	int iRet = -1;
	string sModName( pModName );
	// Change module name characters to lowercase
	ChangeToLower( sModName );
	// Remove variant marks (dots)
	RemoveAllAfterDotIfTwoDots( sModName);
	for( unsigned int i = 0 ; i < m_vDllLoadModList.size() ; i++ )
	{
		string sTemp( m_vDllLoadModList[i].sModuleName );
		ChangeToLower( sTemp );
		// Remove variant marks (dots)
		RemoveAllAfterDotIfTwoDots( sTemp );
		if( sTemp.find( sModName ) != string::npos )
		{
			iRet = i;
			break;
		}	
	}
	return iRet;
}

// -----------------------------------------------------------------------------
// CATDatParser::SetPrintFlag
// -----------------------------------------------------------------------------
void CATDatParser::SetPringFlag( bool bPrintFlag )
{
	LOG_FUNC_ENTRY("CATDatParser::SetPringFlag");
	m_DataSaver.SetPrintFlag( bPrintFlag );
}
// -----------------------------------------------------------------------------
// CATDatParser::SetOffSet
// -----------------------------------------------------------------------------
void CATDatParser::SetOffSet( int iOffSet )
{
	LOG_FUNC_ENTRY("CATDatParser::SetOffSet");
	m_iOffSet = iOffSet;
}

// -----------------------------------------------------------------------------
// CATDatParser::GetOffSet
// -----------------------------------------------------------------------------
int CATDatParser::GetOffSet( ) const
{
	LOG_LOW_FUNC_ENTRY("CATDatParser::GetOffSet");
	return m_iOffSet;
}

// -----------------------------------------------------------------------------
// Set project platform.
// -----------------------------------------------------------------------------
void CATDatParser::SetProjectPlatform( const string& sPlatform )
{
	LOG_FUNC_ENTRY("CATDatParser::SetProjectPlatform");
	m_sProjectPlatform = sPlatform;

	// Check that platform not empty before determing platform from it.
	if ( sPlatform.empty() )
		return;

	// Set functions offset in mapfiles correct (depending on platform).
	if ( _stricmp( sPlatform.c_str(), "armv5" ) == 0 )
	{
		m_iOffSet = FUNCTIONS_OFFSET_IN_MAP_FILE_ARMV5;
	}
	else if ( _stricmp( sPlatform.c_str(), "winscw" ) == 0 )
	{
		m_iOffSet = FUNCTIONS_OFFSET_IN_MAP_FILE_WINSCW;
	}
	else if ( _stricmp( sPlatform.c_str(), "gcce" ) == 0 )
	{
		m_iOffSet = FUNCTIONS_OFFSET_IN_GCCE;
	}
	else
	{
		LOG_STRING( AT_MSG << "Error, cannot set function's offset in map file, invalid platform: " << sPlatform );
	}
}

// -----------------------------------------------------------------------------
// Set projects build type. Use enumeration defined in CATProject.
// -----------------------------------------------------------------------------
void CATDatParser::SetProjectBuildType( int eBuildType )
{
	LOG_FUNC_ENTRY("CATDatParser::SetProjectBuildType");
	m_eProjectBuildType = eBuildType;
}

//EOF
