// DIMEDeserializer.cpp: implementation of the DIMEDeserializer class.
//
//
//////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2002 Robert Cunnings. All rights reserved.
//
// You may copy, modify, distribute or publish this code free of charge.
//
// NO WARRANTY: This software is provided AS IS, without warranty of any kind.
//
//////////////////////////////////////////////////////////////////////
//
// DIMEDeserializer class - implementation of FSM based DIME deserializer.
// A setup call to Init() is required to set it up, and subsequent calls to
// Deserialize() present blocks of data for reading. Finished() returns true
// when the DIME payload has been received in full.
//  
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "DIMEDeserializer.h"
#include <atlconv.h>

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

DIMEDeserializer::DIMEDeserializer()
{
	m_pInputBuffer = NULL;
	m_ulInputBufferSize = 0;
	m_pCache = NULL;
	m_pOptions = NULL;
	m_ulBytesCached = 0;
	m_State = ST_START;
	m_ulCurrentPayload = 0;
	m_pPayloads = NULL;
}

DIMEDeserializer::~DIMEDeserializer()
{
	if (m_pCache != NULL)
	{
		free(m_pCache);
	}
	if (m_pOptions != NULL)
	{
		free(m_pOptions);
	}
}

bool DIMEDeserializer::Init(vector<DIMEPayload *> *pPayloads)
{
	if (pPayloads == NULL)
	{
		m_wstrErrorMsg = L"Deserializer.Initializer.NullPayloadArrayPtr";
		OnEvent(EV_ERROR);
		return false;
	}
	m_pPayloads = pPayloads;
	m_State = ST_START;
	m_ulCurrentPayload = 0;
	if (m_pCache != NULL)
	{
		free(m_pCache);
		m_pCache = NULL;
	}
	m_ulBytesCached = 0;
	m_ulNbrBytesToSkip = 0;
	return true;
}

bool DIMEDeserializer::Deserialize(char *pszBuffer,
								   unsigned long ulSize)
{
	if (m_pPayloads == NULL)
	{
		m_wstrErrorMsg = L"Deserializer.Deserialize.Uninitialized";
		OnEvent(EV_ERROR);
		return false;
	}
	m_pInputBuffer = pszBuffer;
	m_ulInputBufferSize = ulSize;
	// generate event
	OnEvent(EV_DESERIALIZE);
	return true;
}


void DIMEDeserializer::OnEvent(Event e)
{
	Event ev = e;
	while (ev != EV_NULL)
	{
		switch (m_State)
		{
		case ST_START:
			switch (ev)
			{
			case EV_DESERIALIZE:
				m_State = ST_READ_HEADER;
				ev = ACT_Read_Header();
				break;
			case EV_ERROR:
				m_State = ST_ERROR;
				ev = ACT_Error();
				break;
			default:
				m_State = ST_ERROR;
				m_wstrErrorMsg = L"Deserializer.EventHandler.Start.BadEvent";
				ev = ACT_Error();
				break;
			}
			break;
		case ST_READ_HEADER:
			switch (ev)
			{
			case EV_DESERIALIZE:
				ev = ACT_Read_Header();
				break;
			case EV_HEADER_READ:
				m_State = ST_READ_OPTIONS;
				ev = ACT_Read_Options();
				break;
			case EV_ERROR:
				m_State = ST_ERROR;
				ev = ACT_Error();
				break;
			default:
				m_State = ST_ERROR;
				m_wstrErrorMsg = L"Deserializer.EventHandler.ReadHeader.BadEvent";
				ev = ACT_Error();
				break;
			}
			break;
		case ST_READ_CHUNK_HEADER:
			switch (ev)
			{
			case EV_DESERIALIZE:
				ev = ACT_Read_Chunk_Header();
				break;
			case EV_CHUNK_HEADER_READ:
				m_State = ST_READ_CHUNK_OPTIONS;
				ev = ACT_Read_Chunk_Options();
				break;
			case EV_ERROR:
				m_State = ST_ERROR;
				ev = ACT_Error();
				break;
			default:
				m_State = ST_ERROR;
				m_wstrErrorMsg = L"Deserializer.EventHandler.ReadChunkHeader.BadEvent";
				ev = ACT_Error();
				break;
			}
			break;
		case ST_READ_OPTIONS:
			switch (ev)
			{
			case EV_DESERIALIZE:
				ev = ACT_Read_Options();
				break;
			case EV_OPTIONS_READ:
				m_State = ST_READ_ID;
				ev = ACT_Read_Id();
				break;
			case EV_ERROR:
				m_State = ST_ERROR;
				ev = ACT_Error();
				break;
			default:
				m_State = ST_ERROR;
				m_wstrErrorMsg = L"Deserializer.EventHandler.ReadOptions.BadEvent";
				ev = ACT_Error();
				break;
			}
			break;
		case ST_READ_CHUNK_OPTIONS:
			switch (ev)
			{
			case EV_DESERIALIZE:
				ev = ACT_Read_Chunk_Options();
				break;
			case EV_OPTIONS_READ:
				m_State = ST_DESERIALIZE_CHUNK;
				ev = ACT_Deserialize_Chunk();
				break;
			case EV_ERROR:
				m_State = ST_ERROR;
				ev = ACT_Error();
				break;
			default:
				m_State = ST_ERROR;
				m_wstrErrorMsg = L"Deserializer.EventHandler.ReadChunkOptions.BadEvent";
				ev = ACT_Error();
				break;
			}
			break;
		case ST_READ_ID:
			switch (ev)
			{
			case EV_DESERIALIZE:
				ev = ACT_Read_Id();
				break;
			case EV_ID_READ:
				m_State = ST_READ_TYPE;
				ev = ACT_Read_Type();
				break;
			case EV_ERROR:
				m_State = ST_ERROR;
				ev = ACT_Error();
				break;
			default:
				m_State = ST_ERROR;
				m_wstrErrorMsg = L"Deserializer.EventHandler.ReadId.BadEvent";
				ev = ACT_Error();
				break;
			}
			break;
		case ST_READ_TYPE:
			switch (ev)
			{
			case EV_DESERIALIZE:
				ev = ACT_Read_Type();
				break;
			case EV_TYPE_READ:
				m_State = ST_OPEN_PAYLOAD;
				ev = ACT_Open_Payload();
				break;
			case EV_ERROR:
				m_State = ST_ERROR;
				ev = ACT_Error();
				break;
			default:
				m_State = ST_ERROR;
				m_wstrErrorMsg = L"Deserializer.EventHandler.ReadType.BadEvent";
				ev = ACT_Error();
				break;
			}
			break;
		case ST_OPEN_PAYLOAD:
			switch (ev)
			{
			case EV_PAYLOAD_OPENED:
				m_State = ST_DESERIALIZE_PAYLOAD;
				ev = ACT_Deserialize_Payload();
				break;
			case EV_ERROR:
				m_State = ST_ERROR;
				ev = ACT_Error();
				break;
			default:
				m_State = ST_ERROR;
				m_wstrErrorMsg = L"Deserializer.EventHandler.OpenPayload.BadEvent";
				ev = ACT_Error();
				break;
			}
			break;
		case ST_DESERIALIZE_PAYLOAD:
			switch (ev)
			{
			case EV_DESERIALIZE:
				ev = ACT_Deserialize_Payload();
				break;
			case EV_PAYLOAD_END:
				m_State = ST_NEXT_PAYLOAD;
				ev = ACT_Close_Payload();
				break;
			case EV_CHUNK_END:
				m_State = ST_READ_CHUNK_HEADER;
				ev = ACT_Read_Chunk_Header();
				break;
			case EV_MSG_END:
				m_State = ST_FINISH;
				ev = ACT_Close_Payload();
				break;
			case EV_ERROR:
				m_State = ST_ERROR;
				ev = ACT_Error();
				break;
			default:
				m_State = ST_ERROR;
				m_wstrErrorMsg = L"Deserializer.EventHandler.DeserializePayload.BadEvent";
				ev = ACT_Error();
				break;
			}
			break;
		case ST_CLOSE_PAYLOAD:
			switch (ev)
			{
			case EV_PAYLOAD_CLOSED:
				m_State = ST_START;
				ev = EV_DESERIALIZE;
				break;
			case EV_ERROR:
				m_State = ST_ERROR;
				ev = ACT_Error();
				break;
			default:
				m_State = ST_ERROR;
				m_wstrErrorMsg = L"Deserializer.EventHandler.ClosePayload.BadEvent";
				ev = ACT_Error();
				break;
			}
			break;
		case ST_NEXT_PAYLOAD:
			switch (ev)
			{
			case EV_PAYLOAD_CLOSED:
				m_State = ST_START;
				ev = EV_DESERIALIZE;
				break;
			default:
				m_State = ST_ERROR;
				m_wstrErrorMsg = L"Deserializer.EventHandler.NextPayload.BadEvent";
				ev = ACT_Error();
				break;
			}
			break;
		case ST_DESERIALIZE_CHUNK:
			switch (ev)
			{
			case EV_DESERIALIZE:
				ev = ACT_Deserialize_Chunk();
				break;
			case EV_PAYLOAD_END:
				m_State = ST_CLOSE_PAYLOAD;
				ev = ACT_Close_Payload();
				break;
			case EV_MSG_END:
				m_State = ST_FINISH;
				ev = ACT_Close_Payload();
				break;
			case EV_CHUNK_END:
				m_State = ST_READ_CHUNK_HEADER;
				ev = ACT_Read_Chunk_Header();
				break;
			case EV_ERROR:
				m_State = ST_ERROR;
				ev = ACT_Error();
				break;
			default:
				m_State = ST_ERROR;
				m_wstrErrorMsg = L"Deserializer.EventHandler.DeserializeChunk.BadEvent";
				ev = ACT_Error();
				break;
			}
			break;
		case ST_ERROR:
		case ST_FINISH:
			// consume all events
			ev = EV_NULL;
			break;
		default:
			m_State = ST_ERROR;
			m_wstrErrorMsg = L"Deserializer.EventHandler.BadState";
			ev = ACT_Error();
			break;
		}
	}
}

DIMEDeserializer::Event DIMEDeserializer::ACT_Read_Header()
{
	// bytes to skip?
	if (m_ulNbrBytesToSkip > 0)
	{
		m_pInputBuffer += m_ulNbrBytesToSkip;
		m_ulInputBufferSize -= m_ulNbrBytesToSkip;
		m_ulNbrBytesToSkip = 0;
	}
	unsigned long ulBytesAvail = m_ulInputBufferSize + m_ulBytesCached;
	// compute bytes needed to complete header
	unsigned long ulBytesNeeded = HEADER_SIZE - m_ulBytesCached;
	if (ulBytesAvail < ulBytesNeeded)
	{
		memcpy(m_szHeader + m_ulBytesCached, m_pInputBuffer, m_ulInputBufferSize);
		m_ulBytesCached += m_ulInputBufferSize;
		return EV_NULL;
	}
	// copy bytes needed to cache
	memcpy(m_szHeader + m_ulBytesCached, m_pInputBuffer, ulBytesNeeded);
	// reset bytes cached
	m_ulBytesCached = 0;
	// increment input buffer ptr
	m_pInputBuffer += ulBytesNeeded;
	// decrement byte count
	m_ulInputBufferSize -= ulBytesNeeded;

	// process cached header
	Header32 ulHeader;
	// first 32 bits... to little endian
	for (int j = 0; j < 4; j++)
	{
		ulHeader.c[3 - j] = m_szHeader[j];
	}
	// expected version?
	if (((ulHeader.l & MASK_VERSION) >> 27) != 0x01)
	{
		// message start bit not set
		m_wstrErrorMsg = L"Deserializer.ReadHeader.VersionNotSupported1";
		return EV_ERROR;
	}
	// start of message?
	if (m_ulCurrentPayload == 0)
	{
		if (!(ulHeader.l & MASK_MB_FLAG))
		{
			// message start bit not set
			m_wstrErrorMsg = L"Deserializer.ReadHeader.MsgStartBitNotSet";
			return EV_ERROR;
		}
	}
	// end of message?
	m_bEndOfMessage = false;
	if (ulHeader.l & MASK_ME_FLAG)
	{
		// message end bit set
		m_bEndOfMessage = true;
	}
	// chunked?
	m_bCurrentPayloadChunked = false;
	if (ulHeader.l & MASK_CF_FLAG)
	{
		m_bCurrentPayloadChunked = true;
	}
	// get type name format
	m_ulCurrentPayloadTNF = ulHeader.l & MASK_TYPE_T;
	m_ulCurrentPayloadTNF >>= 20;
	// get options length
	m_ulCurrentOptionsLength = ulHeader.l & MASK_OPTIONS_LENGTH;
	m_ulCurrentOptionsPaddingLength = (m_ulCurrentOptionsLength % 4 == 0) ? 0 : (4 - (m_ulCurrentOptionsLength % 4));
	// next 32 bits... to little endian
	for (j = 4; j < 8; j++)
	{
		ulHeader.c[3 - (j - 4)] = m_szHeader[j];
	}
	// get id length
	m_ulCurrentPayloadIdLength = ulHeader.l & MASK_ID_LENGTH;
	m_ulCurrentPayloadIdLength >>= 16;
	m_ulCurrentPayloadIdPaddingLength = (m_ulCurrentPayloadIdLength % 4 == 0) ? 0 : (4 - (m_ulCurrentPayloadIdLength % 4));
	// get type length
	m_ulCurrentPayloadTypeLength = ulHeader.l & MASK_TYPE_LENGTH;
	m_ulCurrentPayloadTypePaddingLength = (m_ulCurrentPayloadTypeLength % 4 == 0) ? 0 : (4 - (m_ulCurrentPayloadTypeLength % 4));
	// next 32 bits... payload length
	for (j = 8; j < 12; j++)
	{
		ulHeader.c[3 - (j - 8)] = m_szHeader[j];
	}
	m_ulCurrentPayloadSize = m_ulPayloadBytesRemaining = ulHeader.l;
	m_ulCurrentPayloadPaddingLength = (m_ulCurrentPayloadSize % 4 == 0) ? 0 : (4 - (m_ulCurrentPayloadSize % 4));
	return EV_HEADER_READ;
}

DIMEDeserializer::Event DIMEDeserializer::ACT_Read_Chunk_Header()
{
	// bytes to skip?
	if (m_ulNbrBytesToSkip > 0)
	{
		m_pInputBuffer += m_ulNbrBytesToSkip;
		m_ulInputBufferSize -= m_ulNbrBytesToSkip;
		m_ulNbrBytesToSkip = 0;
	}
	unsigned long ulBytesAvail = m_ulInputBufferSize + m_ulBytesCached;
	// compute bytes needed to complete chunk header
	unsigned long ulBytesNeeded = HEADER_SIZE - m_ulBytesCached;
	if (ulBytesAvail < ulBytesNeeded)
	{
		// entire header not available yet
		memcpy(m_szHeader + m_ulBytesCached, m_pInputBuffer, m_ulInputBufferSize);
		m_ulBytesCached += m_ulInputBufferSize;
		return EV_NULL;
	}
	// copy bytes needed to cache
	memcpy(m_szHeader + m_ulBytesCached, m_pInputBuffer, ulBytesNeeded);
	// reset bytes cached
	m_ulBytesCached = 0;
	// increment input buffer ptr
	m_pInputBuffer += ulBytesNeeded;
	// decrement byte count
	m_ulInputBufferSize -= ulBytesNeeded;

	// process cached header
	Header32 ulHeader;
	// first 32 bits... to little endian
	for (int j = 0; j < 4; j++)
	{
		ulHeader.c[3 - j] = m_szHeader[j];
	}
	// expected version?
	if (((ulHeader.l & MASK_VERSION) >> 27) != 0x01)
	{
		// message start bit not set
		m_wstrErrorMsg = L"Deserializer.ReadHeader.VersionNotSupported2";
		return EV_ERROR;
	}
	// type_t must be 0x00 for chunk
	if (((ulHeader.l & MASK_TYPE_T) >> 20) != 0x00)
	{
		// no good
		m_wstrErrorMsg = L"Deserializer.ReadHeader.ChunkInvalidType";
		return EV_ERROR;
	}
	// end of message?
	m_bEndOfMessage = false;
	if (ulHeader.l & MASK_ME_FLAG)
	{
		// message end bit set
		m_bEndOfMessage = true;
	}
	// chunked?
	m_bCurrentPayloadChunked = false;
	if (ulHeader.l & MASK_CF_FLAG)
	{
		m_bCurrentPayloadChunked = true;
	}
	// get options length
	m_ulCurrentOptionsLength = ulHeader.l & MASK_OPTIONS_LENGTH;
	m_ulCurrentOptionsPaddingLength = (m_ulCurrentOptionsLength % 4 == 0) ? 0 : (4 - (m_ulCurrentOptionsLength % 4));
	// next 32 bits
	for (j = 4; j < 8; j++)
	{
		ulHeader.c[3 - (j - 4)] = m_szHeader[j];
	}
	// id length and type length must be 0
	if (((ulHeader.l & MASK_ID_LENGTH) >> 16) != 0)
	{
		// no good
		m_wstrErrorMsg = L"Deserializer.ReadHeader.ChunkNonzeroIdLength";
		return EV_ERROR;
	}
	if ((ulHeader.l & MASK_TYPE_LENGTH) != 0)
	{
		// no good
		m_wstrErrorMsg = L"Deserializer.ReadHeader.ChunkNonzeroTypeLength";
		return EV_ERROR;
	}
	// get payload length
	for (j = 8; j < 12; j++)
	{
		ulHeader.c[3 - (j - 8)] = m_szHeader[j];
	}
	m_ulCurrentPayloadSize = m_ulPayloadBytesRemaining = ulHeader.l;
	m_ulCurrentPayloadPaddingLength = (m_ulCurrentPayloadSize % 4 == 0) ? 0 : (4 - (m_ulCurrentPayloadSize % 4));
	return EV_CHUNK_HEADER_READ;
}

DIMEDeserializer::Event DIMEDeserializer::ACT_Read_Options()
{
	if (m_ulCurrentOptionsLength == 0)
	{
		return EV_OPTIONS_READ;
	}
	unsigned long ulBytesAvail = m_ulInputBufferSize + m_ulBytesCached;
	// compute bytes needed to complete options
	unsigned long ulBytesNeeded = (m_ulCurrentOptionsLength + m_ulCurrentOptionsPaddingLength) - m_ulBytesCached;
	if (ulBytesAvail < ulBytesNeeded)
	{
		// entire options block not available yet
		m_pOptions = (char *)realloc(m_pOptions, ulBytesAvail);
		if (m_pOptions == NULL)
		{
			m_wstrErrorMsg = L"Deserializer.ReadOptions.CacheAllocFailed1";
			return EV_ERROR;
		}
		memcpy(m_pOptions + m_ulBytesCached, m_pInputBuffer, m_ulInputBufferSize);
		m_ulBytesCached += m_ulInputBufferSize;
		m_ulInputBufferSize = 0;
		return EV_NULL;
	}
	m_pOptions = (char *)realloc(m_pOptions, m_ulCurrentOptionsLength + m_ulCurrentOptionsPaddingLength);
	if (m_pOptions == NULL)
	{
		m_wstrErrorMsg = L"Deserializer.ReadOptions.CacheAllocFailed2";
		return EV_ERROR;
	}
	memcpy(m_pOptions + m_ulBytesCached, m_pInputBuffer, ulBytesNeeded);
	// increment input buffer ptr
	m_pInputBuffer += (ulBytesNeeded);
	// decrement byte count
	m_ulInputBufferSize -= (ulBytesNeeded);
	// reset cache count
	m_ulBytesCached = 0;
	return EV_OPTIONS_READ;
}

DIMEDeserializer::Event DIMEDeserializer::ACT_Read_Chunk_Options()
{
	return ACT_Read_Options();
}

DIMEDeserializer::Event DIMEDeserializer::ACT_Read_Id()
{
	if (m_ulCurrentPayloadIdLength == 0)
	{
		return EV_ID_READ;
	}
	unsigned long ulBytesAvail = m_ulInputBufferSize + m_ulBytesCached;
	// compute bytes needed to complete id string
	unsigned long ulBytesNeeded = (m_ulCurrentPayloadIdLength + m_ulCurrentPayloadIdPaddingLength) - m_ulBytesCached;
	if (ulBytesAvail < ulBytesNeeded)
	{
		// entire id string not available yet
		m_pCache = (char *)realloc(m_pCache, ulBytesAvail);
		if (m_pCache == NULL)
		{
			m_wstrErrorMsg = L"Deserializer.ReadId.CacheAllocFailed1";
			return EV_ERROR;
		}
		memcpy(m_pCache + m_ulBytesCached, m_pInputBuffer, m_ulInputBufferSize);
		m_ulBytesCached += m_ulInputBufferSize;
		m_ulInputBufferSize = 0;
		return EV_NULL;
	}
	m_pCache = (char *)realloc(m_pCache, m_ulCurrentPayloadIdLength + m_ulCurrentPayloadIdPaddingLength);
	if (m_pCache == NULL)
	{
		m_wstrErrorMsg = L"Deserializer.ReadId.CacheAllocFailed2";
		return EV_ERROR;
	}
	memcpy(m_pCache + m_ulBytesCached, m_pInputBuffer, ulBytesNeeded);
	// increment input buffer ptr
	m_pInputBuffer += (ulBytesNeeded);
	// decrement byte count
	m_ulInputBufferSize -= (ulBytesNeeded);
	// copy id string
	m_strCurrentPayloadId.erase();
	m_strCurrentPayloadId.append(m_pCache, m_ulCurrentPayloadIdLength);
	// done with cache
	free(m_pCache);
	m_pCache = NULL;
	m_ulBytesCached = 0;
	return EV_ID_READ;
}

DIMEDeserializer::Event DIMEDeserializer::ACT_Read_Type()
{
	if (m_ulCurrentPayloadTypeLength == 0)
	{
		return EV_TYPE_READ;
	}
	unsigned long ulBytesAvail = m_ulInputBufferSize + m_ulBytesCached;
	// compute bytes needed to complete id string
	unsigned long ulBytesNeeded = (m_ulCurrentPayloadTypeLength + m_ulCurrentPayloadTypePaddingLength) - m_ulBytesCached;
	if (ulBytesAvail < ulBytesNeeded)
	{
		// entire type string not available yet
		m_pCache = (char *)realloc(m_pCache, ulBytesAvail);
		if (m_pCache == NULL)
		{
			m_wstrErrorMsg = L"Deserializer.ReadType.CacheAllocFailed1";
			return EV_ERROR;
		}
		memcpy(m_pCache + m_ulBytesCached, m_pInputBuffer, m_ulInputBufferSize);
		m_ulBytesCached += m_ulInputBufferSize;
		m_ulInputBufferSize = 0;
		return EV_NULL;
	}
	m_pCache = (char *)realloc(m_pCache, m_ulCurrentPayloadTypeLength + m_ulCurrentPayloadTypePaddingLength);
	if (m_pCache == NULL)
	{
		m_wstrErrorMsg = L"Deserializer.ReadType.CacheAllocFailed2";
		return EV_ERROR;
	}
	memcpy(m_pCache + m_ulBytesCached, m_pInputBuffer, ulBytesNeeded);
	// increment input buffer ptr
	m_pInputBuffer += (ulBytesNeeded);
	// decrement byte count
	m_ulInputBufferSize -= (ulBytesNeeded);
	// copy type id string
	m_strCurrentPayloadType.erase();
	m_strCurrentPayloadType.append(m_pCache, m_ulCurrentPayloadTypeLength);
	// done with cache
	free(m_pCache);
	m_pCache = NULL;
	m_ulBytesCached = 0;
	return EV_TYPE_READ;
}


DIMEDeserializer::Event DIMEDeserializer::ACT_Open_Payload()
{
	IStorage *pIStorage;
	HRESULT hRes = StgCreateDocfile(NULL,
									STGM_DELETEONRELEASE | STGM_DIRECT | STGM_READWRITE| STGM_CREATE | STGM_SHARE_EXCLUSIVE,
									0,
									&pIStorage);
	if (FAILED(hRes))
	{
		m_wstrErrorMsg = L"Deserializer.OpenPayload.CreateFileFailed";
		return EV_ERROR;
	}
	DIMEPayload *pPayload = new DIMEPayload(pIStorage,
											m_strCurrentPayloadId,
											m_strCurrentPayloadType,
											m_ulCurrentPayloadTNF);
	if (pPayload == NULL)
	{
		pIStorage->Release();
		m_wstrErrorMsg = L"Deserializer.OpenPayload.CreatePayloadFailed";
		return EV_ERROR;
	}
	if (pPayload->OpenForWrite() == false)
	{
		m_wstrErrorMsg = L"Deserializer.OpenPayload.OpenStreamFailed";
		return EV_ERROR;
	}
	m_pPayloads->push_back(pPayload);
	pIStorage->Release();
	// process options data
	if (m_ulCurrentOptionsLength > 0)
	{
		if (ProcessOptions(pPayload) == false)
		{
			m_wstrErrorMsg = L"Deserializer.OpenPayload.ProcessingFailed";
			return EV_ERROR;
		}
	}
	return EV_PAYLOAD_OPENED;
}

DIMEDeserializer::Event DIMEDeserializer::ACT_Close_Payload()
{
	DIMEPayload *pPayload = m_pPayloads->at(m_ulCurrentPayload);
	if (pPayload->Close() == false)
	{
		m_wstrErrorMsg = L"Deserializer.ClosePayload.CloseFailed";
		return EV_ERROR;
	}
	++m_ulCurrentPayload;
	return EV_PAYLOAD_CLOSED;
}

DIMEDeserializer::Event DIMEDeserializer::ACT_Deserialize_Payload()
{
	if (m_ulCurrentPayloadSize == 0)
	{
		return EV_PAYLOAD_END;
	}
	DIMEPayload *pPayload = m_pPayloads->at(m_ulCurrentPayload);
	unsigned long ulBytesWritten = 0;
	if ((m_ulPayloadBytesRemaining + m_ulCurrentPayloadPaddingLength) <= m_ulInputBufferSize)
	{
		// enough data in buffer to complete this payload, including padding
		if (pPayload->Write(m_pInputBuffer,
							m_ulPayloadBytesRemaining,
							&ulBytesWritten) == false || ulBytesWritten != m_ulPayloadBytesRemaining)
		{
			m_wstrErrorMsg = L"Deserializer.DeserializePayload.WriteFailed1";
			return EV_ERROR;
		}
		m_ulPayloadBytesRemaining = 0;
		m_pInputBuffer += (ulBytesWritten + m_ulCurrentPayloadPaddingLength);
		m_ulInputBufferSize -= (ulBytesWritten + m_ulCurrentPayloadPaddingLength);
		if (m_bCurrentPayloadChunked == true)
		{
			return EV_CHUNK_END;
		}
		if (m_bEndOfMessage == true)
		{
			return EV_MSG_END;
		}
		return EV_PAYLOAD_END;
	}
	else if (m_ulPayloadBytesRemaining <= m_ulInputBufferSize)
	{
		// payload complete but not all padding is in buffer
		if (pPayload->Write(m_pInputBuffer,
							m_ulPayloadBytesRemaining,
							&ulBytesWritten) == false || ulBytesWritten != m_ulPayloadBytesRemaining)
		{
			m_wstrErrorMsg = L"Deserializer.DeserializePayload.WriteFailed2";
			return EV_ERROR;
		}
		m_ulPayloadBytesRemaining = 0;
		m_pInputBuffer = NULL;
		m_ulInputBufferSize = 0;
		// set number of bytes to skip before next header start
		m_ulNbrBytesToSkip = m_ulCurrentPayloadPaddingLength - (m_ulInputBufferSize - m_ulPayloadBytesRemaining);
		if (m_bCurrentPayloadChunked == true)
		{
			return EV_CHUNK_END;
		}
		if (m_bEndOfMessage == true)
		{
			return EV_MSG_END;
		}
		return EV_PAYLOAD_END;
	}
	else
	{
		// not enough in buffer to complete this payload
		if (pPayload->Write(m_pInputBuffer,
							m_ulInputBufferSize,
							&ulBytesWritten) == false || ulBytesWritten != m_ulInputBufferSize)
		{
			m_wstrErrorMsg = L"Deserializer.DeserializePayload.WriteFailed3";
			return EV_ERROR;
		}
		m_ulPayloadBytesRemaining -= ulBytesWritten;
		m_pInputBuffer = NULL;
		m_ulInputBufferSize = 0;
	}
	return EV_NULL;
}

DIMEDeserializer::Event DIMEDeserializer::ACT_Deserialize_Chunk()
{
	if (m_ulCurrentPayloadSize == 0)
	{
		return EV_PAYLOAD_END;
	}
	DIMEPayload *pPayload = m_pPayloads->at(m_ulCurrentPayload);
	unsigned long ulBytesWritten = 0;
	if ((m_ulPayloadBytesRemaining + m_ulCurrentPayloadPaddingLength) <= m_ulInputBufferSize)
	{
		// enough data in buffer to complete this payload, including padding
		if (pPayload->Write(m_pInputBuffer,
							m_ulPayloadBytesRemaining,
							&ulBytesWritten) == false || ulBytesWritten != m_ulPayloadBytesRemaining)
		{
			m_wstrErrorMsg = L"Deserializer.DeserializeChunk.WriteFailed1";
			return EV_ERROR;
		}
		m_ulPayloadBytesRemaining = 0;
		m_pInputBuffer += (ulBytesWritten + m_ulCurrentPayloadPaddingLength);
		m_ulInputBufferSize -= (ulBytesWritten + m_ulCurrentPayloadPaddingLength);
		if (m_bCurrentPayloadChunked == true)
		{
			return EV_CHUNK_END;
		}
		if (m_bEndOfMessage == true)
		{
			return EV_MSG_END;
		}
		return EV_PAYLOAD_END;
	}
	else if (m_ulPayloadBytesRemaining <= m_ulInputBufferSize)
	{
		// payload complete but not all padding is in buffer
		if (pPayload->Write(m_pInputBuffer,
							m_ulPayloadBytesRemaining,
							&ulBytesWritten) == false || ulBytesWritten != m_ulPayloadBytesRemaining)
		{
			m_wstrErrorMsg = L"Deserializer.DeserializeChunk.WriteFailed2";
			return EV_ERROR;
		}
		m_ulPayloadBytesRemaining = 0;
		m_pInputBuffer = NULL;
		m_ulInputBufferSize = 0;
		// set number of bytes to skip before next header start
		m_ulNbrBytesToSkip = m_ulCurrentPayloadPaddingLength - (m_ulInputBufferSize - m_ulPayloadBytesRemaining);
		if (m_bCurrentPayloadChunked == true)
		{
			return EV_CHUNK_END;
		}
		if (m_bEndOfMessage == true)
		{
			return EV_MSG_END;
		}
		return EV_PAYLOAD_END;
	}
	else
	{
		// not enough in buffer to complete this payload
		if (pPayload->Write(m_pInputBuffer,
							m_ulInputBufferSize,
							&ulBytesWritten) == false || ulBytesWritten != m_ulInputBufferSize)
		{
			m_wstrErrorMsg = L"Deserializer.DeserializeChunk.WriteFailed3";
			return EV_ERROR;
		}
		m_ulPayloadBytesRemaining -= ulBytesWritten;
		m_pInputBuffer = NULL;
		m_ulInputBufferSize = 0;
	}
	return EV_NULL;
}

DIMEDeserializer::Event DIMEDeserializer::ACT_Error()
{
    HANDLE  hEventSource;
    LPTSTR  lpszStrings[1];

	USES_CONVERSION;
	char buf[128];
	_snprintf(buf, 127, "Error: %s", OLE2A(m_wstrErrorMsg.c_str()));
    buf[127] = 0;
    lpszStrings[0] = buf;
    hEventSource = RegisterEventSource(NULL, "White Mesa DIME Parser");
    if (hEventSource != NULL)
    {
        ReportEvent(hEventSource, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (LPCTSTR*) &lpszStrings[0], NULL);
        DeregisterEventSource(hEventSource);
    }
	return EV_ERR_HANDLED;
}

bool DIMEDeserializer::ProcessOptions(DIMEPayload *pPayload)
{
	bool bRtn = pPayload->SetOptions((unsigned char *)m_pOptions, m_ulCurrentOptionsLength);
	// done with cache
	if (m_pOptions != NULL)
	{
		free(m_pOptions);
		m_pOptions = NULL;
	}
	return bRtn;
}
