bool CVMTDoc::SetVector2DParamValue( CDmAttribute *pAttribute, const char *pValue )
{
	Vector2D vec;
	int count = sscanf( pValue, " [ %f %f ]", &vec[0], &vec[1] );
	if ( count == 2 )
	{
		pAttribute->SetValue( vec );
		return true;
	}

	count = sscanf( pValue, " { %f %f }", &vec[0], &vec[1] );
	if ( count == 2 )
	{
		vec /= 255.0f;
		pAttribute->SetValue( vec );
		return true;
	}

	return false;
}

#include "vmtdoc.h"
#include "tier1/keyvalues.h"
#include "tier1/utlbuffer.h"
#include "datamodel/dmelement.h"
#include "vmttool.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/ishader.h"
#include "toolutils/enginetools_int.h"
#include "filesystem.h"

// Standard properties
struct StandardParam_t
{
	const char *m_pParamName;
	ShaderParamType_t m_ParamType;
	const char *m_pDefaultValue;
	const char *m_pWidgetType;
	const char *m_pTextType;
};

// NOTE: All entries in here must have all-lowercase param names!
static StandardParam_t g_pStandardParams[] =
{
	{ "$surfaceprop", SHADER_PARAM_TYPE_STRING, "default", "surfacepropertypicker", "surfacePropertyName" },
	{ "%detailtype", SHADER_PARAM_TYPE_STRING, "", "detailtypepicker", "detailTypeName" },
	{ "%compilesky", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilehint", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compileskip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compileorigin", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compileclip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%playerclip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilenpcclip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilenochop", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compiletrigger", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilenolight", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compileplayercontrolclip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compileladder", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilewet", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilenodraw", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compileinvisible", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilenonsolid", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compiledetail", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilewater", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compileslime", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ NULL, SHADER_PARAM_TYPE_BOOL, NULL, NULL, NULL }
};

// Constructor
CVMTDoc::CVMTDoc( IVMTDocCallback *pCallback ) : m_pCallback( pCallback )
{
	m_hRoot = NULL;
	m_pFileName[0] = 0;
	m_bDirty = false;
	m_pCurrentIShader = NULL;

	KeyValues *pKeyValues = new KeyValues( "Wireframe" );
	m_pScratchMaterial.Init( "VMT Preview", pKeyValues );
	g_pDataModel->InstallNotificationCallback( this );
}

CVMTDoc::~CVMTDoc()
{
	if ( m_hRoot.Get() )
	{
		RemoveAllShaderParams( m_hRoot );
	}
	g_pDataModel->RemoveNotificationCallback( this );
}

// Inherited from INotifyUI
void CVMTDoc::NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
{
	OnDataChanged( pReason, nNotifySource, nNotifyFlags );
}

// Gets the file name
const char *CVMTDoc::GetFileName()
{
	return m_pFileName;
}

void CVMTDoc::SetFileName( const char *pFileName )
{
	Q_strncpy( m_pFileName, pFileName, sizeof( m_pFileName ) );
	Q_FixSlashes( m_pFileName );
	SetDirty( true );
}

// Dirty bits
void CVMTDoc::SetDirty( bool bDirty )
{
	m_bDirty = bDirty;
}

bool CVMTDoc::IsDirty() const
{
	return m_bDirty;
}

// Creates the root element
bool CVMTDoc::CreateRootElement()
{
	Assert( !m_hRoot.Get() );

	DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( GetFileName() );

	// Create the main element
	m_hRoot = g_pDataModel->CreateElement( "DmElement", GetFileName(), fileid );
	if ( m_hRoot == DMELEMENT_HANDLE_INVALID )
		return false;

	g_pDataModel->SetFileRoot( fileid, m_hRoot );

	// Each VMT list needs to have an editortype associated with it so it displays nicely in editors
	m_hRoot->SetValue( "editorType", "vmt" );
	m_hRoot->AddAttribute( "proxies", AT_ELEMENT_ARRAY );
	m_hRoot->AddAttribute( "fallbacks", AT_ELEMENT_ARRAY );

	m_pCallback->RemoveAllToolParameters();

	// Add standard parameters
	for ( int i = 0; g_pStandardParams[i].m_pParamName; ++i )
	{
		AddNewShaderParam( m_hRoot, g_pStandardParams[i].m_pParamName, g_pStandardParams[i].m_ParamType,
			g_pStandardParams[i].m_pDefaultValue );

		if ( g_pStandardParams[i].m_pParamName[0] == '%' )
		{
			m_pCallback->AddToolParameter( g_pStandardParams[i].m_pParamName, g_pStandardParams[i].m_pWidgetType, g_pStandardParams[i].m_pTextType );
		}
		else if ( g_pStandardParams[i].m_pWidgetType || g_pStandardParams[i].m_pTextType )
		{
			m_pCallback->RemoveShaderParameter( g_pStandardParams[i].m_pParamName );
			m_pCallback->AddShaderParameter( g_pStandardParams[i].m_pParamName, g_pStandardParams[i].m_pWidgetType, g_pStandardParams[i].m_pTextType );
		}
	}

	return true;
}

// Creates a new VMT
void CVMTDoc::CreateNew()
{
	Assert( !m_hRoot.Get() );

	// This is not undoable
	CAppDisableUndoScopeGuard guard( "CVMTDoc::CreateNew", NOTIFY_CHANGE_OTHER );

	Q_strncpy( m_pFileName, "untitled", sizeof( m_pFileName ) );

	// Create the main element
	if ( !CreateRootElement() )
		return;

	m_pPreviewMaterial.Init( m_pScratchMaterial );

	SetShader( "wireframe" );
	SetDirty( false );
}

// Copies VMT parameters into the root
void CVMTDoc::CopyParamsFromVMT( CDmElement *pVMT )
{
	// First, set the shader parameters
	SetShader( pVMT->GetValueString( "shader" ) );

	// Now, copy the shader parameters over
	CDmAttribute* pSrc;
	CDmAttribute* pDst;
	for ( pSrc = pVMT->FirstAttribute(); pSrc; pSrc = pSrc->NextAttribute() )
	{
		// Only copy shader parameters
		if ( !IsShaderParam( pSrc ) )
			continue;

		// Adds the attribute if it doesn't exist
		const char *pSrcName = pSrc->GetName();
		if ( !m_hRoot->HasAttribute( pSrcName ) )
		{
			m_hRoot->AddAttribute( pSrcName, pSrc->GetType() );
		}
		pDst = m_hRoot->GetAttribute( pSrcName );
		pDst->AddFlag( FATTRIB_USERDEFINED );

		DmAttributeType_t srcType = pSrc->GetType();
		DmAttributeType_t dstType = pDst->GetType();
		if ( dstType == srcType )
		{
			pDst->SetValue( pSrc );
			continue;
		}

		// Certain type conversions are allowed
		switch( dstType )
		{
		case AT_BOOL:
			if ( srcType == AT_INT )
			{
				pDst->SetValue( pSrc );
			}
			break;

		case AT_INT:
			if ( srcType == AT_BOOL )
			{
				pDst->SetValue( pSrc );
			}
			break;

		case AT_COLOR:
			if ( srcType == AT_VECTOR3 )
			{
				Color c;
				int r, g, b;
				Vector v = pSrc->GetValue<Vector>( );
				v *= 255.0f;
				r = clamp( v[0], 0, 255 );
				g = clamp( v[1], 0, 255 );
				b = clamp( v[2], 0, 255 );
				c.SetColor( r, g, b, 255 );
				pDst->SetValue( c );
			}
			break;
		}
	}

	// Any shader parameter that isn't in the VMT make undefined
	for ( pDst = m_hRoot->FirstAttribute(); pDst; pDst = pDst->NextAttribute() )
	{
		if ( !IsShaderParam( pDst ) )
			continue;

		if ( !pVMT->HasAttribute( pDst->GetName() ) )
		{
			// Special hack for alpha + colors
			if ( !Q_stricmp( pDst->GetName(), "$alpha" ) )
			{
				pDst->SetValue( 1.0f );
			}
			else if ( pDst->GetType() == AT_COLOR )
			{
				Color c( 255, 255, 255, 255 );
				pDst->SetValue( c );
			}
			else
			{
				pDst->SetToDefaultValue();
			}
		}
	}
}

// Hooks the preview to an existing material, if there is one
void CVMTDoc::SetupPreviewMaterial( )
{
	// Extract a material name from the material
	char pLocalName[MAX_PATH];

	// relative paths can be passed in for in-game material picking
	if ( !g_pFileSystem->FullPathToRelativePath( m_pFileName, pLocalName, sizeof(pLocalName) ) )
	{
		Q_strcpy( pLocalName, m_pFileName );
	}

	if ( Q_strnicmp( pLocalName, "materials", 9 ) )
		goto noMaterialConnection;

	char pMaterialName[MAX_PATH];
	Q_StripExtension( pLocalName + 10, pMaterialName, sizeof(pMaterialName) );
	IMaterial *pMaterial = g_pMaterialSystem->FindMaterial( pMaterialName, "Editable material", false );
	if ( !pMaterial || pMaterial->IsErrorMaterial() )
		goto noMaterialConnection;

	m_pPreviewMaterial.Init( pMaterial );
	return;

noMaterialConnection:
	m_pPreviewMaterial.Init( m_pScratchMaterial );
}

// Saves/loads from file
bool CVMTDoc::LoadFromFile( const char *pFileName )
{
	Assert( !m_hRoot.Get() );

	SetDirty( false );

	Q_strncpy( m_pFileName, pFileName, sizeof( m_pFileName ) );
	if ( !m_pFileName[0] )
		return false;

	// This is not undoable
	CAppDisableUndoScopeGuard guard( "CVMTDoc::LoadFromFile", NOTIFY_CHANGE_OTHER );

	// Create the main element
	if ( !CreateRootElement() )
		return false;

	// change the filename of all the elements under the root, so we can unload the imported elements later
	DmFileId_t rootFileId = g_pDataModel->GetFileId( m_pFileName );
	g_pDataModel->SetFileName( rootFileId, "<temp>" );

	// This will allow us to edit in context!
	SetupPreviewMaterial( );

	CDmElement *pIVMT = NULL;
	g_pDataModel->RestoreFromFile( m_pFileName, NULL, "vmt", &pIVMT );
	CDmElement *pVMT = CastElement< CDmElement >( pIVMT );
	if ( !pVMT )
		return false;

	// FIXME: This is necessary so that all shader parameters appear in
	// the same order, with the same type, as what you'd get using File->New.
	// If we added a dependency to the material system into dmserializers,
	// we could avoid this work here (I think!).
	CopyParamsFromVMT( pVMT );

	// unload the imported elements and change the root's filename back
	DmFileId_t vmtFileId = g_pDataModel->GetFileId( m_pFileName );
	g_pDataModel->RemoveFileId( vmtFileId );
	g_pDataModel->SetFileName( rootFileId, m_pFileName );

	SetDirty( false );
	return true;
}

// Prior to saving to disk, extract all shader parameters which == the default
CDmElement* CVMTDoc::ExtractDefaultParameters( )
{
	CDmElement *pMaterial = m_hRoot->Copy( );

	CDmAttribute* pAttribute = pMaterial->FirstAttribute();
	CDmAttribute* pNextAttribute = NULL;
	for ( ; pAttribute; pAttribute = pNextAttribute )
	{
		pNextAttribute = pAttribute->NextAttribute();

		const char *pShaderParam = pAttribute->GetName();

		// Check for standard params
		int i;
		for ( i = 0; g_pStandardParams[i].m_pParamName != NULL; ++i )
		{
			if ( !Q_stricmp( g_pStandardParams[i].m_pParamName, pShaderParam ) )
			{
				char temp[512];
				CUtlBuffer buf( temp, sizeof(temp), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE );
				pAttribute->Serialize( buf );

				if ( !Q_stricmp( (char*)buf.Base(), g_pStandardParams[i].m_pDefaultValue ) )
				{
					// Buffers match! Therefore it's still using the default parameter
					pMaterial->RemoveAttributeByPtr( pAttribute );
				}
				break;
			}
		}

		// Standard attribute found, continue
		if ( g_pStandardParams[i].m_pParamName )
			continue;

		// Only remove shader parameters
		if ( !IsShaderParam( pAttribute ) )
			continue;

		// Remove flags whose value is 0
		int nCount = g_pMaterialSystem->ShaderFlagCount();
		for ( i = 0; i < nCount; ++i )
		{
			const char *pFlagName = g_pMaterialSystem->ShaderFlagName( i );
			if ( !Q_stricmp( pShaderParam, pFlagName ) )
				break;
		}

		// It's a flag! Remove the attribute if its value is 0
		if ( i != nCount )
		{
			if ( pAttribute->GetValue<bool>( ) == 0 )
			{
				pMaterial->RemoveAttributeByPtr( pAttribute );
			}
			continue;
		}

		// FIXME: We can't do this.. the defaults in the strings need to be changed to
		// make it so they actually match the true defaults
		continue;

		// Remove parameters which match the default value
		nCount = m_pCurrentIShader->GetNumParams();
		for ( i = 0; i < nCount; ++i )
		{
			// FIXME: Check type matches
			if ( Q_stricmp( pShaderParam, m_pCurrentIShader->GetParamName( i ) ) )
				continue;

			// NOTE: This isn't particularly efficient. Too bad!
			// It's hard to do efficiently owing to all the import conversion
			char temp[512];
			char temp2[512];
			CUtlBuffer buf( temp, sizeof(temp), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE );
			CUtlBuffer buf2( temp2, sizeof(temp2), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE );
			pAttribute->Serialize( buf );
			SetAttributeValueFromDefault( pMaterial, pAttribute, m_pCurrentIShader->GetParamDefault( i ) );
			pAttribute->Serialize( buf2 );

			if ( ( buf.TellMaxPut() == buf2.TellMaxPut() ) && !memcmp( buf.Base(), buf2.Base(), buf.TellMaxPut() ) )
			{
				// Buffers match! Therefore it's still using the default parameter
				pMaterial->RemoveAttributeByPtr( pAttribute );
			}
			else
			{
				// Restore the actual value
				pAttribute->Unserialize( buf );
			}
			break;
		}
	}

	return pMaterial;
}

// Saves to disk
bool CVMTDoc::SaveToFile( )
{
	if ( m_hRoot.Get() && m_pFileName && m_pFileName[0] )
	{
		CDisableUndoScopeGuard guard;
		CDmElement *pSaveRoot = ExtractDefaultParameters();
		bool bOk = g_pDataModel->SaveToFile( m_pFileName, NULL, "keyvalues", "vmt", pSaveRoot );
		DestroyElement( pSaveRoot, TD_DEEP );
		if ( !bOk )
			return false;
	}

	SetDirty( false );
	return true;
}

// Finds a shader
IShader *CVMTDoc::FindShader( const char *pShaderName )
{
	int nCount = g_pMaterialSystem->ShaderCount();
	IShader **ppShaderList = (IShader**)_alloca( nCount * sizeof(IShader*) );
	g_pMaterialSystem->GetShaders( 0, nCount, ppShaderList );
	for ( int i = 0; i < nCount; ++i )
	{
		if ( !Q_stricmp( pShaderName, ppShaderList[i]->GetName() ) )
			return ppShaderList[i];
	}
	return NULL;
}

// Is this attribute a shader parameter?
bool CVMTDoc::IsShaderParam( CDmAttribute* pAttribute )
{
	const char *pName = pAttribute->GetName();

	// Shader params start with a $ or %
	if ( pName[0] != '$' && pName[0] != '%' )
		return false;

	// Don't remove name, type, or id
	if ( pAttribute->IsFlagSet( FATTRIB_STANDARD ) )
		return false;

	// All shader params have USERDEFINED set
	if ( !pAttribute->IsFlagSet( FATTRIB_USERDEFINED ) )
		return false;

	// Don't remove arrays... those aren't shader parameters
	if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
		return false;

	// Standard params aren't counted here
	for ( int i = 0; g_pStandardParams[i].m_pParamName; ++i )
	{
		if ( !Q_stricmp( g_pStandardParams[i].m_pParamName, pName ) )
			return false;
	}

	return true;
}

// Remove all shader parameters
void CVMTDoc::RemoveAllShaderParams( CDmElement *pMaterial )
{
	CDmAttribute* pAttribute;
	CDmAttribute* pNextAttribute = NULL;
	for ( pAttribute = pMaterial->FirstAttribute(); pAttribute; pAttribute = pNextAttribute )
	{
		pNextAttribute = pAttribute->NextAttribute();

		// Only remove shader parameters
		if ( !IsShaderParam( pAttribute ) )
			continue;

		m_pCallback->RemoveShaderParameter( pAttribute->GetName() );
		pMaterial->RemoveAttributeByPtr( pAttribute );
	}
}

// Remove all shader parameters that don't exist in the new shader
void CVMTDoc::RemoveUnusedShaderParams( CDmElement *pMaterial, IShader *pShader, IShader *pOldShader )
{
	CDmAttribute* pAttribute = pMaterial->FirstAttribute();
	CDmAttribute* pNextAttribute = NULL;
	for ( ; pAttribute; pAttribute = pNextAttribute )
	{
		pNextAttribute = pAttribute->NextAttribute();

		// Only remove shader parameters
		if ( !IsShaderParam( pAttribute ) )
			continue;

		// Don't remove flags
		int nCount = g_pMaterialSystem->ShaderFlagCount();
		int i;
		for ( i = 0; i < nCount; ++i )
		{
			const char *pFlagName = g_pMaterialSystem->ShaderFlagName( i );
			if ( !Q_stricmp( pAttribute->GetName(), pFlagName ) )
				break;
		}

		if ( i != nCount )
			continue;

		const char *pShaderParam = pAttribute->GetName();

		// Remove parameters we've currently got but which don't exist in the new shader
		nCount = pShader->GetNumParams();
		for ( i = 0; i < nCount; ++i )
		{
			// FIXME: Check type matches
			if ( !Q_stricmp( pShaderParam, pShader->GetParamName( i ) ) )
				break;
		}

		// No match? Remove it!
		if ( i == nCount )
		{
			m_pCallback->RemoveShaderParameter( pAttribute->GetName() );
			pMaterial->RemoveAttributeByPtr( pAttribute );
			continue;
		}

		// Remove parameters from the old shader which match the default value
		// This will make the default values update to the new shader's defaults
		if ( pOldShader )
		{
			nCount = pOldShader->GetNumParams();
			for ( i = 0; i < nCount; ++i )
			{
				// FIXME: Check type matches
				if ( Q_stricmp( pShaderParam, pOldShader->GetParamName( i ) ) )
					continue;

				// NOTE: This isn't particularly efficient. Too bad!
				// It's hard to do efficiently owing to all the import conversion
				char temp1[512];
				char temp2[512];
				CUtlBuffer buf1( temp1, sizeof(temp1), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE );
				CUtlBuffer buf2( temp2, sizeof(temp2), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE );
				pAttribute->Serialize( buf1 );
				SetAttributeValueFromDefault( pMaterial, pAttribute, pOldShader->GetParamDefault( i ) );
				pAttribute->Serialize( buf2 );

				if ( ( buf1.TellMaxPut() == buf2.TellMaxPut() ) && !memcmp( buf1.Base(), buf2.Base(), buf1.TellMaxPut() ) )
				{
					// Buffers match! Therefore it's still using the default parameter
					m_pCallback->RemoveShaderParameter( pAttribute->GetName() );
					pMaterial->RemoveAttributeByPtr( pAttribute );
				}
				else
				{
					pAttribute->Unserialize( buf1 );
				}
				break;
			}
		}
	}
}

// Add attribute for shader parameter
CDmAttribute* CVMTDoc::AddAttributeForShaderParameter( CDmElement *pMaterial, const char *pParamName, ShaderParamType_t paramType )
{
	CDmAttribute *pAttribute = NULL;
	switch ( paramType )
	{
	case SHADER_PARAM_TYPE_INTEGER:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_INT );
		break;

	case SHADER_PARAM_TYPE_BOOL:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_BOOL );
		break;

 	case SHADER_PARAM_TYPE_FLOAT:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_FLOAT );
		break;

	case SHADER_PARAM_TYPE_STRING:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_STRING );
		break;

	case SHADER_PARAM_TYPE_COLOR:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_COLOR );
		break;

	case SHADER_PARAM_TYPE_VEC2:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_VECTOR2 );
		break;

	case SHADER_PARAM_TYPE_VEC3:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_VECTOR3 );
		break;

	case SHADER_PARAM_TYPE_VEC4:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_VECTOR4 );
		break;

	case SHADER_PARAM_TYPE_FOURCC:
		Assert( 0 );
		break;

	case SHADER_PARAM_TYPE_MATRIX:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_VMATRIX );
		break;

	case SHADER_PARAM_TYPE_TEXTURE:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_STRING );
		m_pCallback->AddShaderParameter( pParamName, "vtfpicker", "vtfName" );
		break;

	case SHADER_PARAM_TYPE_MATERIAL:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_STRING );
		m_pCallback->AddShaderParameter( pParamName, "vmtpicker", "vmtName" );
		break;

	default:
		break;
	}

	if ( pAttribute )
	{
		pAttribute->AddFlag( FATTRIB_USERDEFINED );
	}

	return pAttribute;
}

// A couple methods to set vmatrix param values from strings (OLD METHOD!)
bool CVMTDoc::SetVMatrixParamValue( CDmAttribute *pAttribute, const char *pValue )
{
	// FIXME: Change default strings to match DME?
	// Then we could remove this crap
	VMatrix mat;
	int count = sscanf( pValue, " [ %f %f %f %f  %f %f %f %f  %f %f %f %f  %f %f %f %f ]",
		&mat.m[0][0], &mat.m[0][1], &mat.m[0][2], &mat.m[0][3],
		&mat.m[1][0], &mat.m[1][1], &mat.m[1][2], &mat.m[1][3],
		&mat.m[2][0], &mat.m[2][1], &mat.m[2][2], &mat.m[2][3],
		&mat.m[3][0], &mat.m[3][1], &mat.m[3][2], &mat.m[3][3] );
	if (count == 16)
	{
		pAttribute->SetValue( mat );
		return true;
	}

	Vector2D scale, center;
	float angle;
	Vector2D translation;
	count = sscanf( pValue, " center %f %f scale %f %f rotate %f translate %f %f",
		&center.x, &center.y, &scale.x, &scale.y, &angle, &translation.x, &translation.y );
	if (count != 7)
		return false;

	VMatrix temp;
	MatrixBuildTranslation( mat, -center.x, -center.y, 0.0f );
	MatrixBuildScale( temp, scale.x, scale.y, 1.0f );
	MatrixMultiply( temp, mat, mat );
	MatrixBuildRotateZ( temp, angle );
	MatrixMultiply( temp, mat, mat );
	MatrixBuildTranslation( temp, center.x + translation.x, center.y + translation.y, 0.0f );
	MatrixMultiply( temp, mat, mat );
	pAttribute->SetValue( mat );
	return true;
}

// A couple methods to set vmatrix param values from strings (OLD METHOD!)

// A couple methods to set vmatrix param values from strings (OLD METHOD!)
bool CVMTDoc::SetVector3DParamValue( CDmAttribute *pAttribute, const char *pValue )
{
	Vector vec;
	int count = sscanf( pValue, " [ %f %f %f ]", &vec[0], &vec[1], &vec[2] );
	if ( count == 3 )
	{
		pAttribute->SetValue( vec );
		return true;
	}

	count = sscanf( pValue, " { %f %f %f }", &vec[0], &vec[1], &vec[2] );
	if ( count == 3 )
	{
		vec /= 255.0f;
		pAttribute->SetValue( vec );
		return true;
	}

	return false;
}

// A couple methods to set vmatrix param values from strings (OLD METHOD!)
bool CVMTDoc::SetVector4DParamValue( CDmAttribute *pAttribute, const char *pValue )
{
	Vector4D vec;
	int count = sscanf( pValue, " [ %f %f %f %f ]", &vec[0], &vec[1], &vec[2], &vec[3] );
	if ( count == 4 )
	{
		pAttribute->SetValue( vec );
		return true;
	}

	count = sscanf( pValue, " { %f %f %f %f }", &vec[0], &vec[1], &vec[2], &vec[3] );
	if ( count == 4 )
	{
		vec /= 255.0f;
		pAttribute->SetValue( vec );
		return true;
	}

	return false;
}

// A couple methods to set vmatrix param values from strings (OLD METHOD!)
bool CVMTDoc::SetColorParamValue( CDmAttribute *pAttribute, const char *pValue )
{
	Color c;
	int r, g, b;
	Vector vec;
	int count = sscanf( pValue, " [ %f %f %f ]", &vec[0], &vec[1], &vec[2] );
	if ( count == 3 )
	{
		vec *= 255.0f;
		r = clamp( vec[0], 0, 255 );
		g = clamp( vec[1], 0, 255 );
		b = clamp( vec[2], 0, 255 );
		c.SetColor( r, g, b, 255 );
		pAttribute->SetValue( c );
		return true;
	}

	count = sscanf( pValue, " { %d %d %d }", &r, &g, &b );
	if ( count == 3 )
	{
		c.SetColor( r, g, b, 255 );
		pAttribute->SetValue( c );
		return true;
	}

	return false;
}

// Sets an attribute value from the shader param default
void CVMTDoc::SetAttributeValueFromDefault( CDmElement *pMaterial, CDmAttribute *pAttribute, const char *pValue )
{
	// FIXME: Change default strings to match DME?
	// Then we could remove this crap
	switch ( pAttribute->GetType() )
	{
	case AT_VMATRIX:
		if ( SetVMatrixParamValue( pAttribute, pValue ) )
			return;
		break;
	case AT_COLOR:
		if ( SetColorParamValue( pAttribute, pValue ) )
			return;
		break;
	case AT_VECTOR2:
		if ( SetVector2DParamValue( pAttribute, pValue ) )
			return;
		break;
	case AT_VECTOR3:
		if ( SetVector3DParamValue( pAttribute, pValue ) )
			return;
		break;
	case AT_VECTOR4:
		if ( SetVector4DParamValue( pAttribute, pValue ) )
			return;
		break;
	}

	pMaterial->SetValueFromString( pAttribute->GetName(), pValue );
}

// Add a single shader parameter if it doesn't exist
void CVMTDoc::AddNewShaderParam( CDmElement *pMaterial, const char *pParamName, ShaderParamType_t paramType, const char *pValue )
{
	char temp[512];
	Q_strncpy( temp, pParamName, sizeof(temp) );
	Q_strlower( temp );
	pParamName = temp;

	CDmAttribute* pAttribute = NULL;
	for ( pAttribute = pMaterial->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
	{
		// Don't bother testing against name, type, or id
		if ( pAttribute->IsFlagSet( FATTRIB_STANDARD ) )
			continue;

		const char *pAttributeName = pAttribute->GetName();
		if ( !Q_stricmp( pAttributeName, pParamName ) )
			return;
	}

	// No match? Add it!
	pAttribute = AddAttributeForShaderParameter( pMaterial, pParamName, paramType );
	if ( pAttribute )
	{
		SetAttributeValueFromDefault( pMaterial, pAttribute, pValue );
	}
}

// Add all shader parameters that don't currently exist
void CVMTDoc::AddNewShaderParams( CDmElement *pMaterial, IShader *pShader )
{
	// First add all flags
	m_pCallback->RemoveAllFlagParameters();
	int nCount = g_pMaterialSystem->ShaderFlagCount();
 	int i;
	for ( i = 0; i < nCount; ++i )
	{
		const char *pParamName = g_pMaterialSystem->ShaderFlagName( i );
		AddNewShaderParam( pMaterial, pParamName, SHADER_PARAM_TYPE_BOOL, "0" );
		m_pCallback->AddFlagParameter( pParamName );
	}

	// Next add all shader-specific parameters
	nCount = pShader->GetNumParams();
	for ( i = 0; i < nCount; ++i )
	{
		const char *pParamName = pShader->GetParamName( i );

		// Don't add parameters that don't want to be editable
		if ( pShader->GetParamFlags( i ) & SHADER_PARAM_NOT_EDITABLE )
			continue;

		ShaderParamType_t paramType = pShader->GetParamType( i );
		const char *pDefault = pShader->GetParamDefault( i );
		AddNewShaderParam( pMaterial, pParamName, paramType, pDefault );
	}
}

// Sets shader parameters to the default for that shader
void CVMTDoc::SetParamsToDefault()
{
	// This is undoable
	CAppUndoScopeGuard guard( 0, "Set Params to Default", "Set Params to Default" );

	// Next add all shader-specific parameters
	int nCount = m_pCurrentIShader->GetNumParams();
	for ( int i = 0; i < nCount; ++i )
	{
		const char *pParamName = m_pCurrentIShader->GetParamName( i );

		// Don't set parameters that don't want to be editable
		if ( m_pCurrentIShader->GetParamFlags( i ) & SHADER_PARAM_NOT_EDITABLE )
			continue;

		char pAttributeName[512];
		Q_strncpy( pAttributeName, pParamName, sizeof(pAttributeName) );
		Q_strlower( pAttributeName );

		if ( !m_hRoot->HasAttribute( pAttributeName ) )
			continue;

		CDmAttribute *pAttribute = m_hRoot->GetAttribute( pAttributeName );
		const char *pDefault = m_pCurrentIShader->GetParamDefault( i );
		SetAttributeValueFromDefault( m_hRoot, pAttribute, pDefault );
	}
}

// Sets the shader in the material
void CVMTDoc::SetShader( const char *pShaderName )
{
	// No change? don't bother
	if ( !Q_stricmp( m_CurrentShader, pShaderName ) )
		return;

	m_CurrentShader = pShaderName;

	// This is undoable
	CAppUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Set Shader", "Set Shader" );

	char pActualShaderName[512];
	g_pMaterialSystem->GetShaderFallback( pShaderName, pActualShaderName, sizeof(pActualShaderName) );

	m_hRoot->SetValue( "shader", pShaderName );

	// First, find the shader
	IShader *pShader = FindShader( pActualShaderName );

	// Remove all shader parameters that don't exist in the new shader
	// And also remove shader parameters that do match the default value
	RemoveUnusedShaderParams( m_hRoot, pShader, m_pCurrentIShader );

	// Add all shader parameters that don't currently exist
	AddNewShaderParams( m_hRoot, pShader );

	m_pCurrentIShader = pShader;
}

// Gets the preview material
IMaterial *CVMTDoc::GetPreviewMaterial()
{
	return m_pPreviewMaterial;
}

// Updates the preview material
void CVMTDoc::UpdatePreviewMaterial()
{
	if ( !m_hRoot.Get() )
		return;

	// Update all shader parameters
	SetShader( m_hRoot->GetValueString( "shader" ) );

	// Use the file conversion to write to a text format
	char buf[1024];
	CUtlBuffer vmtBuf( buf, sizeof(buf), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE );
	g_pDataModel->Serialize( vmtBuf, "vmt", "vmt", m_hRoot );

	// Now use the text format to create a keyvalues
	KeyValues *pVMTKeyValues = new KeyValues( "ShaderName" );
	pVMTKeyValues->LoadFromBuffer( "VMT Preview", vmtBuf, g_pFileSystem, "GAME" );

	// Finally, hook the keyvalues into the material.
	m_pPreviewMaterial->SetShaderAndParams( pVMTKeyValues );
	pVMTKeyValues->deleteThis();
}

// Returns the root object
CDmElement *CVMTDoc::GetRootObject()
{
	return m_hRoot;
}

// Called when data changes
void CVMTDoc::OnDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
{
	SetDirty( nNotifyFlags & NOTIFY_SETDIRTYFLAG ? true : false );
	UpdatePreviewMaterial();
	m_pCallback->OnDocChanged( pReason, nNotifySource, nNotifyFlags );
}


switch ( pAttribute->GetType() )
	{
	case AT_VMATRIX:
		if ( SetVMatrixParamValue( pAttribute, pValue ) )
			return;
		break;
	case AT_COLOR:
		if ( SetColorParamValue( pAttribute, pValue ) )
			return;
		break;
	case AT_VECTOR2:
		if ( SetVector2DParamValue( pAttribute, pValue ) )
			return;
		break;
	case AT_VECTOR3:
		if ( SetVector3DParamValue( pAttribute, pValue ) )
			return;
		break;
	case AT_VECTOR4:
		if ( SetVector4DParamValue( pAttribute, pValue ) )
			return;
		break;
	}

#include "vmtdoc.h"
#include "tier1/keyvalues.h"
#include "tier1/utlbuffer.h"
#include "datamodel/dmelement.h"
#include "vmttool.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/ishader.h"
#include "toolutils/enginetools_int.h"
#include "filesystem.h"

// Standard properties
struct StandardParam_t
{
	const char *m_pParamName;
	ShaderParamType_t m_ParamType;
	const char *m_pDefaultValue;
	const char *m_pWidgetType;
	const char *m_pTextType;
};

// NOTE: All entries in here must have all-lowercase param names!
static StandardParam_t g_pStandardParams[] =
{
	{ "$surfaceprop", SHADER_PARAM_TYPE_STRING, "default", "surfacepropertypicker", "surfacePropertyName" },
	{ "%detailtype", SHADER_PARAM_TYPE_STRING, "", "detailtypepicker", "detailTypeName" },
	{ "%compilesky", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilehint", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compileskip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compileorigin", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compileclip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%playerclip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilenpcclip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilenochop", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compiletrigger", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilenolight", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compileplayercontrolclip", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compileladder", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilewet", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilenodraw", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compileinvisible", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilenonsolid", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compiledetail", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compilewater", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ "%compileslime", SHADER_PARAM_TYPE_BOOL, "0", NULL, NULL },
	{ NULL, SHADER_PARAM_TYPE_BOOL, NULL, NULL, NULL }
};

// Constructor
CVMTDoc::CVMTDoc( IVMTDocCallback *pCallback ) : m_pCallback( pCallback )
{
	m_hRoot = NULL;
	m_pFileName[0] = 0;
	m_bDirty = false;
	m_pCurrentIShader = NULL;

	KeyValues *pKeyValues = new KeyValues( "Wireframe" );
	m_pScratchMaterial.Init( "VMT Preview", pKeyValues );
	g_pDataModel->InstallNotificationCallback( this );
}

CVMTDoc::~CVMTDoc()
{
	if ( m_hRoot.Get() )
	{
		RemoveAllShaderParams( m_hRoot );
	}
	g_pDataModel->RemoveNotificationCallback( this );
}

// Inherited from INotifyUI
void CVMTDoc::NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
{
	OnDataChanged( pReason, nNotifySource, nNotifyFlags );
}

// Gets the file name
const char *CVMTDoc::GetFileName()
{
	return m_pFileName;
}

void CVMTDoc::SetFileName( const char *pFileName )
{
	Q_strncpy( m_pFileName, pFileName, sizeof( m_pFileName ) );
	Q_FixSlashes( m_pFileName );
	SetDirty( true );
}

// Dirty bits
void CVMTDoc::SetDirty( bool bDirty )
{
	m_bDirty = bDirty;
}

bool CVMTDoc::IsDirty() const
{
	return m_bDirty;
}

// Creates the root element
bool CVMTDoc::CreateRootElement()
{
	Assert( !m_hRoot.Get() );

	DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( GetFileName() );

	// Create the main element
	m_hRoot = g_pDataModel->CreateElement( "DmElement", GetFileName(), fileid );
	if ( m_hRoot == DMELEMENT_HANDLE_INVALID )
		return false;

	g_pDataModel->SetFileRoot( fileid, m_hRoot );

	// Each VMT list needs to have an editortype associated with it so it displays nicely in editors
	m_hRoot->SetValue( "editorType", "vmt" );
	m_hRoot->AddAttribute( "proxies", AT_ELEMENT_ARRAY );
	m_hRoot->AddAttribute( "fallbacks", AT_ELEMENT_ARRAY );

	m_pCallback->RemoveAllToolParameters();

	// Add standard parameters
	for ( int i = 0; g_pStandardParams[i].m_pParamName; ++i )
	{
		AddNewShaderParam( m_hRoot, g_pStandardParams[i].m_pParamName, g_pStandardParams[i].m_ParamType,
			g_pStandardParams[i].m_pDefaultValue );

		if ( g_pStandardParams[i].m_pParamName[0] == '%' )
		{
			m_pCallback->AddToolParameter( g_pStandardParams[i].m_pParamName, g_pStandardParams[i].m_pWidgetType, g_pStandardParams[i].m_pTextType );
		}
		else if ( g_pStandardParams[i].m_pWidgetType || g_pStandardParams[i].m_pTextType )
		{
			m_pCallback->RemoveShaderParameter( g_pStandardParams[i].m_pParamName );
			m_pCallback->AddShaderParameter( g_pStandardParams[i].m_pParamName, g_pStandardParams[i].m_pWidgetType, g_pStandardParams[i].m_pTextType );
		}
	}

	return true;
}

// Creates a new VMT
void CVMTDoc::CreateNew()
{
	Assert( !m_hRoot.Get() );

	// This is not undoable
	CAppDisableUndoScopeGuard guard( "CVMTDoc::CreateNew", NOTIFY_CHANGE_OTHER );

	Q_strncpy( m_pFileName, "untitled", sizeof( m_pFileName ) );

	// Create the main element
	if ( !CreateRootElement() )
		return;

	m_pPreviewMaterial.Init( m_pScratchMaterial );

	SetShader( "wireframe" );
	SetDirty( false );
}

// Copies VMT parameters into the root
void CVMTDoc::CopyParamsFromVMT( CDmElement *pVMT )
{
	// First, set the shader parameters
	SetShader( pVMT->GetValueString( "shader" ) );

	// Now, copy the shader parameters over
	CDmAttribute* pSrc;
	CDmAttribute* pDst;
	for ( pSrc = pVMT->FirstAttribute(); pSrc; pSrc = pSrc->NextAttribute() )
	{
		// Only copy shader parameters
		if ( !IsShaderParam( pSrc ) )
			continue;

		// Adds the attribute if it doesn't exist
		const char *pSrcName = pSrc->GetName();
		if ( !m_hRoot->HasAttribute( pSrcName ) )
		{
			m_hRoot->AddAttribute( pSrcName, pSrc->GetType() );
		}
		pDst = m_hRoot->GetAttribute( pSrcName );
		pDst->AddFlag( FATTRIB_USERDEFINED );

		DmAttributeType_t srcType = pSrc->GetType();
		DmAttributeType_t dstType = pDst->GetType();
		if ( dstType == srcType )
		{
			pDst->SetValue( pSrc );
			continue;
		}

		// Certain type conversions are allowed
		switch( dstType )
		{
		case AT_BOOL:
			if ( srcType == AT_INT )
			{
				pDst->SetValue( pSrc );
			}
			break;

		case AT_INT:
			if ( srcType == AT_BOOL )
			{
				pDst->SetValue( pSrc );
			}
			break;

		case AT_COLOR:
			if ( srcType == AT_VECTOR3 )
			{
				Color c;
				int r, g, b;
				Vector v = pSrc->GetValue<Vector>( );
				v *= 255.0f;
				r = clamp( v[0], 0, 255 );
				g = clamp( v[1], 0, 255 );
				b = clamp( v[2], 0, 255 );
				c.SetColor( r, g, b, 255 );
				pDst->SetValue( c );
			}
			break;
		}
	}

	// Any shader parameter that isn't in the VMT make undefined
	for ( pDst = m_hRoot->FirstAttribute(); pDst; pDst = pDst->NextAttribute() )
	{
		if ( !IsShaderParam( pDst ) )
			continue;

		if ( !pVMT->HasAttribute( pDst->GetName() ) )
		{
			// Special hack for alpha + colors
			if ( !Q_stricmp( pDst->GetName(), "$alpha" ) )
			{
				pDst->SetValue( 1.0f );
			}
			else if ( pDst->GetType() == AT_COLOR )
			{
				Color c( 255, 255, 255, 255 );
				pDst->SetValue( c );
			}
			else
			{
				pDst->SetToDefaultValue();
			}
		}
	}
}

// Hooks the preview to an existing material, if there is one
void CVMTDoc::SetupPreviewMaterial( )
{
	// Extract a material name from the material
	char pLocalName[MAX_PATH];

	// relative paths can be passed in for in-game material picking
	if ( !g_pFileSystem->FullPathToRelativePath( m_pFileName, pLocalName, sizeof(pLocalName) ) )
	{
		Q_strcpy( pLocalName, m_pFileName );
	}

	if ( Q_strnicmp( pLocalName, "materials", 9 ) )
		goto noMaterialConnection;

	char pMaterialName[MAX_PATH];
	Q_StripExtension( pLocalName + 10, pMaterialName, sizeof(pMaterialName) );
	IMaterial *pMaterial = g_pMaterialSystem->FindMaterial( pMaterialName, "Editable material", false );
	if ( !pMaterial || pMaterial->IsErrorMaterial() )
		goto noMaterialConnection;

	m_pPreviewMaterial.Init( pMaterial );
	return;

noMaterialConnection:
	m_pPreviewMaterial.Init( m_pScratchMaterial );
}

// Saves/loads from file
bool CVMTDoc::LoadFromFile( const char *pFileName )
{
	Assert( !m_hRoot.Get() );

	SetDirty( false );

	Q_strncpy( m_pFileName, pFileName, sizeof( m_pFileName ) );
	if ( !m_pFileName[0] )
		return false;

	// This is not undoable
	CAppDisableUndoScopeGuard guard( "CVMTDoc::LoadFromFile", NOTIFY_CHANGE_OTHER );

	// Create the main element
	if ( !CreateRootElement() )
		return false;

	// change the filename of all the elements under the root, so we can unload the imported elements later
	DmFileId_t rootFileId = g_pDataModel->GetFileId( m_pFileName );
	g_pDataModel->SetFileName( rootFileId, "<temp>" );

	// This will allow us to edit in context!
	SetupPreviewMaterial( );

	CDmElement *pIVMT = NULL;
	g_pDataModel->RestoreFromFile( m_pFileName, NULL, "vmt", &pIVMT );
	CDmElement *pVMT = CastElement< CDmElement >( pIVMT );
	if ( !pVMT )
		return false;

	// FIXME: This is necessary so that all shader parameters appear in
	// the same order, with the same type, as what you'd get using File->New.
	// If we added a dependency to the material system into dmserializers,
	// we could avoid this work here (I think!).
	CopyParamsFromVMT( pVMT );

	// unload the imported elements and change the root's filename back
	DmFileId_t vmtFileId = g_pDataModel->GetFileId( m_pFileName );
	g_pDataModel->RemoveFileId( vmtFileId );
	g_pDataModel->SetFileName( rootFileId, m_pFileName );

	SetDirty( false );
	return true;
}

// Prior to saving to disk, extract all shader parameters which == the default
CDmElement* CVMTDoc::ExtractDefaultParameters( )
{
	CDmElement *pMaterial = m_hRoot->Copy( );

	CDmAttribute* pAttribute = pMaterial->FirstAttribute();
	CDmAttribute* pNextAttribute = NULL;
	for ( ; pAttribute; pAttribute = pNextAttribute )
	{
		pNextAttribute = pAttribute->NextAttribute();

		const char *pShaderParam = pAttribute->GetName();

		// Check for standard params
		int i;
		for ( i = 0; g_pStandardParams[i].m_pParamName != NULL; ++i )
		{
			if ( !Q_stricmp( g_pStandardParams[i].m_pParamName, pShaderParam ) )
			{
				char temp[512];
				CUtlBuffer buf( temp, sizeof(temp), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE );
				pAttribute->Serialize( buf );

				if ( !Q_stricmp( (char*)buf.Base(), g_pStandardParams[i].m_pDefaultValue ) )
				{
					// Buffers match! Therefore it's still using the default parameter
					pMaterial->RemoveAttributeByPtr( pAttribute );
				}
				break;
			}
		}

		// Standard attribute found, continue
		if ( g_pStandardParams[i].m_pParamName )
			continue;

		// Only remove shader parameters
		if ( !IsShaderParam( pAttribute ) )
			continue;

		// Remove flags whose value is 0
		int nCount = g_pMaterialSystem->ShaderFlagCount();
		for ( i = 0; i < nCount; ++i )
		{
			const char *pFlagName = g_pMaterialSystem->ShaderFlagName( i );
			if ( !Q_stricmp( pShaderParam, pFlagName ) )
				break;
		}

		// It's a flag! Remove the attribute if its value is 0
		if ( i != nCount )
		{
			if ( pAttribute->GetValue<bool>( ) == 0 )
			{
				pMaterial->RemoveAttributeByPtr( pAttribute );
			}
			continue;
		}

		// FIXME: We can't do this.. the defaults in the strings need to be changed to
		// make it so they actually match the true defaults
		continue;

		// Remove parameters which match the default value
		nCount = m_pCurrentIShader->GetNumParams();
		for ( i = 0; i < nCount; ++i )
		{
			// FIXME: Check type matches
			if ( Q_stricmp( pShaderParam, m_pCurrentIShader->GetParamName( i ) ) )
				continue;

			// NOTE: This isn't particularly efficient. Too bad!
			// It's hard to do efficiently owing to all the import conversion
			char temp[512];
			char temp2[512];
			CUtlBuffer buf( temp, sizeof(temp), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE );
			CUtlBuffer buf2( temp2, sizeof(temp2), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE );
			pAttribute->Serialize( buf );
			SetAttributeValueFromDefault( pMaterial, pAttribute, m_pCurrentIShader->GetParamDefault( i ) );
			pAttribute->Serialize( buf2 );

			if ( ( buf.TellMaxPut() == buf2.TellMaxPut() ) && !memcmp( buf.Base(), buf2.Base(), buf.TellMaxPut() ) )
			{
				// Buffers match! Therefore it's still using the default parameter
				pMaterial->RemoveAttributeByPtr( pAttribute );
			}
			else
			{
				// Restore the actual value
				pAttribute->Unserialize( buf );
			}
			break;
		}
	}

	return pMaterial;
}

// Saves to disk
bool CVMTDoc::SaveToFile( )
{
	if ( m_hRoot.Get() && m_pFileName && m_pFileName[0] )
	{
		CDisableUndoScopeGuard guard;
		CDmElement *pSaveRoot = ExtractDefaultParameters();
		bool bOk = g_pDataModel->SaveToFile( m_pFileName, NULL, "keyvalues", "vmt", pSaveRoot );
		DestroyElement( pSaveRoot, TD_DEEP );
		if ( !bOk )
			return false;
	}

	SetDirty( false );
	return true;
}

// Finds a shader
IShader *CVMTDoc::FindShader( const char *pShaderName )
{
	int nCount = g_pMaterialSystem->ShaderCount();
	IShader **ppShaderList = (IShader**)_alloca( nCount * sizeof(IShader*) );
	g_pMaterialSystem->GetShaders( 0, nCount, ppShaderList );
	for ( int i = 0; i < nCount; ++i )
	{
		if ( !Q_stricmp( pShaderName, ppShaderList[i]->GetName() ) )
			return ppShaderList[i];
	}
	return NULL;
}

// Is this attribute a shader parameter?
bool CVMTDoc::IsShaderParam( CDmAttribute* pAttribute )
{
	const char *pName = pAttribute->GetName();

	// Shader params start with a $ or %
	if ( pName[0] != '$' && pName[0] != '%' )
		return false;

	// Don't remove name, type, or id
	if ( pAttribute->IsFlagSet( FATTRIB_STANDARD ) )
		return false;

	// All shader params have USERDEFINED set
	if ( !pAttribute->IsFlagSet( FATTRIB_USERDEFINED ) )
		return false;

	// Don't remove arrays... those aren't shader parameters
	if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
		return false;

	// Standard params aren't counted here
	for ( int i = 0; g_pStandardParams[i].m_pParamName; ++i )
	{
		if ( !Q_stricmp( g_pStandardParams[i].m_pParamName, pName ) )
			return false;
	}

	return true;
}

// Remove all shader parameters
void CVMTDoc::RemoveAllShaderParams( CDmElement *pMaterial )
{
	CDmAttribute* pAttribute;
	CDmAttribute* pNextAttribute = NULL;
	for ( pAttribute = pMaterial->FirstAttribute(); pAttribute; pAttribute = pNextAttribute )
	{
		pNextAttribute = pAttribute->NextAttribute();

		// Only remove shader parameters
		if ( !IsShaderParam( pAttribute ) )
			continue;

		m_pCallback->RemoveShaderParameter( pAttribute->GetName() );
		pMaterial->RemoveAttributeByPtr( pAttribute );
	}
}

// Remove all shader parameters that don't exist in the new shader
void CVMTDoc::RemoveUnusedShaderParams( CDmElement *pMaterial, IShader *pShader, IShader *pOldShader )
{
	CDmAttribute* pAttribute = pMaterial->FirstAttribute();
	CDmAttribute* pNextAttribute = NULL;
	for ( ; pAttribute; pAttribute = pNextAttribute )
	{
		pNextAttribute = pAttribute->NextAttribute();

		// Only remove shader parameters
		if ( !IsShaderParam( pAttribute ) )
			continue;

		// Don't remove flags
		int nCount = g_pMaterialSystem->ShaderFlagCount();
		int i;
		for ( i = 0; i < nCount; ++i )
		{
			const char *pFlagName = g_pMaterialSystem->ShaderFlagName( i );
			if ( !Q_stricmp( pAttribute->GetName(), pFlagName ) )
				break;
		}

		if ( i != nCount )
			continue;

		const char *pShaderParam = pAttribute->GetName();

		// Remove parameters we've currently got but which don't exist in the new shader
		nCount = pShader->GetNumParams();
		for ( i = 0; i < nCount; ++i )
		{
			// FIXME: Check type matches
			if ( !Q_stricmp( pShaderParam, pShader->GetParamName( i ) ) )
				break;
		}

		// No match? Remove it!
		if ( i == nCount )
		{
			m_pCallback->RemoveShaderParameter( pAttribute->GetName() );
			pMaterial->RemoveAttributeByPtr( pAttribute );
			continue;
		}

		// Remove parameters from the old shader which match the default value
		// This will make the default values update to the new shader's defaults
		if ( pOldShader )
		{
			nCount = pOldShader->GetNumParams();
			for ( i = 0; i < nCount; ++i )
			{
				// FIXME: Check type matches
				if ( Q_stricmp( pShaderParam, pOldShader->GetParamName( i ) ) )
					continue;

				// NOTE: This isn't particularly efficient. Too bad!
				// It's hard to do efficiently owing to all the import conversion
				char temp1[512];
				char temp2[512];
				CUtlBuffer buf1( temp1, sizeof(temp1), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE );
				CUtlBuffer buf2( temp2, sizeof(temp2), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE );
				pAttribute->Serialize( buf1 );
				SetAttributeValueFromDefault( pMaterial, pAttribute, pOldShader->GetParamDefault( i ) );
				pAttribute->Serialize( buf2 );

				if ( ( buf1.TellMaxPut() == buf2.TellMaxPut() ) && !memcmp( buf1.Base(), buf2.Base(), buf1.TellMaxPut() ) )
				{
					// Buffers match! Therefore it's still using the default parameter
					m_pCallback->RemoveShaderParameter( pAttribute->GetName() );
					pMaterial->RemoveAttributeByPtr( pAttribute );
				}
				else
				{
					pAttribute->Unserialize( buf1 );
				}
				break;
			}
		}
	}
}

// Add attribute for shader parameter
CDmAttribute* CVMTDoc::AddAttributeForShaderParameter( CDmElement *pMaterial, const char *pParamName, ShaderParamType_t paramType )
{
	CDmAttribute *pAttribute = NULL;
	switch ( paramType )
	{
	case SHADER_PARAM_TYPE_INTEGER:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_INT );
		break;

	case SHADER_PARAM_TYPE_BOOL:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_BOOL );
		break;

 	case SHADER_PARAM_TYPE_FLOAT:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_FLOAT );
		break;

	case SHADER_PARAM_TYPE_STRING:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_STRING );
		break;

	case SHADER_PARAM_TYPE_COLOR:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_COLOR );
		break;

	case SHADER_PARAM_TYPE_VEC2:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_VECTOR2 );
		break;

	case SHADER_PARAM_TYPE_VEC3:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_VECTOR3 );
		break;

	case SHADER_PARAM_TYPE_VEC4:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_VECTOR4 );
		break;

	case SHADER_PARAM_TYPE_FOURCC:
		Assert( 0 );
		break;

	case SHADER_PARAM_TYPE_MATRIX:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_VMATRIX );
		break;

	case SHADER_PARAM_TYPE_TEXTURE:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_STRING );
		m_pCallback->AddShaderParameter( pParamName, "vtfpicker", "vtfName" );
		break;

	case SHADER_PARAM_TYPE_MATERIAL:
		pAttribute = pMaterial->AddAttribute( pParamName, AT_STRING );
		m_pCallback->AddShaderParameter( pParamName, "vmtpicker", "vmtName" );
		break;

	default:
		break;
	}

	if ( pAttribute )
	{
		pAttribute->AddFlag( FATTRIB_USERDEFINED );
	}

	return pAttribute;
}

// A couple methods to set vmatrix param values from strings (OLD METHOD!)
bool CVMTDoc::SetVMatrixParamValue( CDmAttribute *pAttribute, const char *pValue )
{
	// FIXME: Change default strings to match DME?
	// Then we could remove this crap
	VMatrix mat;
	int count = sscanf( pValue, " [ %f %f %f %f  %f %f %f %f  %f %f %f %f  %f %f %f %f ]",
		&mat.m[0][0], &mat.m[0][1], &mat.m[0][2], &mat.m[0][3],
		&mat.m[1][0], &mat.m[1][1], &mat.m[1][2], &mat.m[1][3],
		&mat.m[2][0], &mat.m[2][1], &mat.m[2][2], &mat.m[2][3],
		&mat.m[3][0], &mat.m[3][1], &mat.m[3][2], &mat.m[3][3] );
	if (count == 16)
	{
		pAttribute->SetValue( mat );
		return true;
	}

	Vector2D scale, center;
	float angle;
	Vector2D translation;
	count = sscanf( pValue, " center %f %f scale %f %f rotate %f translate %f %f",
		&center.x, &center.y, &scale.x, &scale.y, &angle, &translation.x, &translation.y );
	if (count != 7)
		return false;

	VMatrix temp;
	MatrixBuildTranslation( mat, -center.x, -center.y, 0.0f );
	MatrixBuildScale( temp, scale.x, scale.y, 1.0f );
	MatrixMultiply( temp, mat, mat );
	MatrixBuildRotateZ( temp, angle );
	MatrixMultiply( temp, mat, mat );
	MatrixBuildTranslation( temp, center.x + translation.x, center.y + translation.y, 0.0f );
	MatrixMultiply( temp, mat, mat );
	pAttribute->SetValue( mat );
	return true;
}

// A couple methods to set vmatrix param values from strings (OLD METHOD!)
bool CVMTDoc::SetVector2DParamValue( CDmAttribute *pAttribute, const char *pValue )
{
	Vector2D vec;
	int count = sscanf( pValue, " [ %f %f ]", &vec[0], &vec[1] );
	if ( count == 2 )
	{
		pAttribute->SetValue( vec );
		return true;
	}

	count = sscanf( pValue, " { %f %f }", &vec[0], &vec[1] );
	if ( count == 2 )
	{
		vec /= 255.0f;
		pAttribute->SetValue( vec );
		return true;
	}

	return false;
}

// A couple methods to set vmatrix param values from strings (OLD METHOD!)
bool CVMTDoc::SetVector3DParamValue( CDmAttribute *pAttribute, const char *pValue )
{
	Vector vec;
	int count = sscanf( pValue, " [ %f %f %f ]", &vec[0], &vec[1], &vec[2] );
	if ( count == 3 )
	{
		pAttribute->SetValue( vec );
		return true;
	}

	count = sscanf( pValue, " { %f %f %f }", &vec[0], &vec[1], &vec[2] );
	if ( count == 3 )
	{
		vec /= 255.0f;
		pAttribute->SetValue( vec );
		return true;
	}

	return false;
}

// A couple methods to set vmatrix param values from strings (OLD METHOD!)
bool CVMTDoc::SetVector4DParamValue( CDmAttribute *pAttribute, const char *pValue )
{
	Vector4D vec;
	int count = sscanf( pValue, " [ %f %f %f %f ]", &vec[0], &vec[1], &vec[2], &vec[3] );
	if ( count == 4 )
	{
		pAttribute->SetValue( vec );
		return true;
	}

	count = sscanf( pValue, " { %f %f %f %f }", &vec[0], &vec[1], &vec[2], &vec[3] );
	if ( count == 4 )
	{
		vec /= 255.0f;
		pAttribute->SetValue( vec );
		return true;
	}

	return false;
}

// A couple methods to set vmatrix param values from strings (OLD METHOD!)
bool CVMTDoc::SetColorParamValue( CDmAttribute *pAttribute, const char *pValue )
{
	Color c;
	int r, g, b;
	Vector vec;
	int count = sscanf( pValue, " [ %f %f %f ]", &vec[0], &vec[1], &vec[2] );
	if ( count == 3 )
	{
		vec *= 255.0f;
		r = clamp( vec[0], 0, 255 );
		g = clamp( vec[1], 0, 255 );
		b = clamp( vec[2], 0, 255 );
		c.SetColor( r, g, b, 255 );
		pAttribute->SetValue( c );
		return true;
	}

	count = sscanf( pValue, " { %d %d %d }", &r, &g, &b );
	if ( count == 3 )
	{
		c.SetColor( r, g, b, 255 );
		pAttribute->SetValue( c );
		return true;
	}

	return false;
}

// Sets an attribute value from the shader param default
void CVMTDoc::SetAttributeValueFromDefault( CDmElement *pMaterial, CDmAttribute *pAttribute, const char *pValue )
{
	// FIXME: Change default strings to match DME?
	// Then we could remove this crap

	pMaterial->SetValueFromString( pAttribute->GetName(), pValue );
}

// Add a single shader parameter if it doesn't exist
void CVMTDoc::AddNewShaderParam( CDmElement *pMaterial, const char *pParamName, ShaderParamType_t paramType, const char *pValue )
{
	char temp[512];
	Q_strncpy( temp, pParamName, sizeof(temp) );
	Q_strlower( temp );
	pParamName = temp;

	CDmAttribute* pAttribute = NULL;
	for ( pAttribute = pMaterial->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
	{
		// Don't bother testing against name, type, or id
		if ( pAttribute->IsFlagSet( FATTRIB_STANDARD ) )
			continue;

		const char *pAttributeName = pAttribute->GetName();
		if ( !Q_stricmp( pAttributeName, pParamName ) )
			return;
	}

	// No match? Add it!
	pAttribute = AddAttributeForShaderParameter( pMaterial, pParamName, paramType );
	if ( pAttribute )
	{
		SetAttributeValueFromDefault( pMaterial, pAttribute, pValue );
	}
}

// Add all shader parameters that don't currently exist
void CVMTDoc::AddNewShaderParams( CDmElement *pMaterial, IShader *pShader )
{
	// First add all flags
	m_pCallback->RemoveAllFlagParameters();
	int nCount = g_pMaterialSystem->ShaderFlagCount();
 	int i;
	for ( i = 0; i < nCount; ++i )
	{
		const char *pParamName = g_pMaterialSystem->ShaderFlagName( i );
		AddNewShaderParam( pMaterial, pParamName, SHADER_PARAM_TYPE_BOOL, "0" );
		m_pCallback->AddFlagParameter( pParamName );
	}

	// Next add all shader-specific parameters
	nCount = pShader->GetNumParams();
	for ( i = 0; i < nCount; ++i )
	{
		const char *pParamName = pShader->GetParamName( i );

		// Don't add parameters that don't want to be editable
		if ( pShader->GetParamFlags( i ) & SHADER_PARAM_NOT_EDITABLE )
			continue;

		ShaderParamType_t paramType = pShader->GetParamType( i );
		const char *pDefault = pShader->GetParamDefault( i );
		AddNewShaderParam( pMaterial, pParamName, paramType, pDefault );
	}
}

// Sets shader parameters to the default for that shader
void CVMTDoc::SetParamsToDefault()
{
	// This is undoable
	CAppUndoScopeGuard guard( 0, "Set Params to Default", "Set Params to Default" );

	// Next add all shader-specific parameters
	int nCount = m_pCurrentIShader->GetNumParams();
	for ( int i = 0; i < nCount; ++i )
	{
		const char *pParamName = m_pCurrentIShader->GetParamName( i );

		// Don't set parameters that don't want to be editable
		if ( m_pCurrentIShader->GetParamFlags( i ) & SHADER_PARAM_NOT_EDITABLE )
			continue;

		char pAttributeName[512];
		Q_strncpy( pAttributeName, pParamName, sizeof(pAttributeName) );
		Q_strlower( pAttributeName );

		if ( !m_hRoot->HasAttribute( pAttributeName ) )
			continue;

		CDmAttribute *pAttribute = m_hRoot->GetAttribute( pAttributeName );
		const char *pDefault = m_pCurrentIShader->GetParamDefault( i );
		SetAttributeValueFromDefault( m_hRoot, pAttribute, pDefault );
	}
}

// Sets the shader in the material
void CVMTDoc::SetShader( const char *pShaderName )
{
	// No change? don't bother
	if ( !Q_stricmp( m_CurrentShader, pShaderName ) )
		return;

	m_CurrentShader = pShaderName;

	// This is undoable
	CAppUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Set Shader", "Set Shader" );

	char pActualShaderName[512];
	g_pMaterialSystem->GetShaderFallback( pShaderName, pActualShaderName, sizeof(pActualShaderName) );

	m_hRoot->SetValue( "shader", pShaderName );

	// First, find the shader
	IShader *pShader = FindShader( pActualShaderName );

	// Remove all shader parameters that don't exist in the new shader
	// And also remove shader parameters that do match the default value
	RemoveUnusedShaderParams( m_hRoot, pShader, m_pCurrentIShader );

	// Add all shader parameters that don't currently exist
	AddNewShaderParams( m_hRoot, pShader );

	m_pCurrentIShader = pShader;
}

// Gets the preview material
IMaterial *CVMTDoc::GetPreviewMaterial()
{
	return m_pPreviewMaterial;
}

// Updates the preview material
void CVMTDoc::UpdatePreviewMaterial()
{
	if ( !m_hRoot.Get() )
		return;

	// Update all shader parameters
	SetShader( m_hRoot->GetValueString( "shader" ) );

	// Use the file conversion to write to a text format
	char buf[1024];
	CUtlBuffer vmtBuf( buf, sizeof(buf), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::EXTERNAL_GROWABLE );
	g_pDataModel->Serialize( vmtBuf, "vmt", "vmt", m_hRoot );

	// Now use the text format to create a keyvalues
	KeyValues *pVMTKeyValues = new KeyValues( "ShaderName" );
	pVMTKeyValues->LoadFromBuffer( "VMT Preview", vmtBuf, g_pFileSystem, "GAME" );

	// Finally, hook the keyvalues into the material.
	m_pPreviewMaterial->SetShaderAndParams( pVMTKeyValues );
	pVMTKeyValues->deleteThis();
}

// Returns the root object
CDmElement *CVMTDoc::GetRootObject()
{
	return m_hRoot;
}

// Called when data changes
void CVMTDoc::OnDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
{
	SetDirty( nNotifyFlags & NOTIFY_SETDIRTYFLAG ? true : false );
	UpdatePreviewMaterial();
	m_pCallback->OnDocChanged( pReason, nNotifySource, nNotifyFlags );
}


{
    auto signature = getPrivateKey().sign(TW::Hash::sha256(data), TWCurveNIST256p1);
    signature.pop_back();
    return signature;
}

#include "Signer.h"
#include "Script.h"
#include "../Hash.h"
#include "../HexCoding.h"
#include "../PrivateKey.h"
#include "../PublicKey.h"
#include "../proto/NEO.pb.h"
#include "../proto/Common.pb.h"

using namespace TW;
using namespace TW::NEO;
using namespace std;

Signer::Signer(const PrivateKey& priKey) : privateKey(std::move(priKey)) {
    auto pub = privateKey.getPublicKey(TWPublicKeyTypeNIST256p1);
    publicKey = pub.bytes;
    address = Address(pub);
}

PrivateKey Signer::getPrivateKey() const {
    return privateKey;
}

TW::PublicKey Signer::getPublicKey() const {
    return TW::PublicKey(publicKey, TWPublicKeyTypeNIST256p1);
}

Address Signer::getAddress() const {
    return address;
}

void Signer::sign(Transaction& tx) const {
    auto unsignedTx = tx.serialize();
    auto signature = sign(unsignedTx);
    tx.witnesses.clear();
    Witness witness;
    witness.invocationScript = Script::CreateInvocationScript(signature);
    witness.verificationScript = Script::CreateSignatureRedeemScript(publicKey);
    tx.witnesses.push_back(witness);
}

Data Signer::sign(const Data& data) const

Proto::TransactionPlan Signer::plan(const Proto::SigningInput& input) {
    Proto::TransactionPlan plan;
    try {
        std::map<std::string, int64_t> available;
        std::map<std::string, int64_t> required;

        for (int i = 0; i < input.outputs_size(); i++) {
            required[input.outputs(i).asset_id()] = input.outputs(i).amount();
        }

        for (int i = 0; i < input.inputs_size(); i++) {
            if (available.find(input.inputs(i).asset_id()) == available.end()) {
                available[input.inputs(i).asset_id()] = input.inputs(i).value();
                plan.add_inputs()->MergeFrom(input.inputs(i));
                continue;
            }

            if (input.inputs(i).asset_id() != input.gas_asset_id() &&
                required[input.inputs(i).asset_id()] < available[input.inputs(i).asset_id()]) {
                continue;
            }

            available[input.inputs(i).asset_id()] += input.inputs(i).value();
            plan.add_inputs()->MergeFrom(input.inputs(i));
        }

        int existGASTransfer = -1;
        for (int i = 0; i < input.outputs_size(); i++) {
            auto* outputPlan = plan.add_outputs();

            if (available.find(input.inputs(i).asset_id()) == available.end() ||
                available[input.outputs(i).asset_id()] < input.outputs(i).amount()) {
                throw Common::Proto::SigningError(Common::Proto::Error_low_balance);
            }

            if (input.outputs(i).asset_id() == input.gas_asset_id()) {
                existGASTransfer = i;
            }

            int64_t availableAmount = available[input.outputs(i).asset_id()];
            outputPlan->set_available_amount(availableAmount);
            outputPlan->set_amount(input.outputs(i).amount());
            outputPlan->set_change(availableAmount - input.outputs(i).amount());

            outputPlan->set_to_address(input.outputs(i).to_address());
            outputPlan->set_asset_id(input.outputs(i).asset_id());
            outputPlan->set_change_address(input.outputs(i).change_address());
        }

        const int64_t SIGNATURE_SIZE = 103;
        int64_t transactionSize =
            prepareUnsignedTransaction(input, plan, false).serialize().size() + SIGNATURE_SIZE;

        const int64_t LARGE_TX_SIZE = 1024;
        const int64_t MIN_FEE_FOR_LARGE_TX = 100000;
        const int64_t FEE_FOR_BYTE_OF_TX = 1000;

        bool feeNeed = input.fee() > 0;
        if (transactionSize >= LARGE_TX_SIZE) {
            feeNeed = true;
        }
        if (feeNeed && existGASTransfer < 0) {
            auto* outputPlan = plan.add_outputs();
            existGASTransfer = plan.outputs_size() - 1;

            if (available.find(input.gas_asset_id()) == available.end() ||
                available[input.gas_asset_id()] < 1024) {
                throw Common::Proto::SigningError(Common::Proto::Error_tx_too_big);
            }

            int64_t availableAmount = available[input.gas_asset_id()];
            outputPlan->set_available_amount(availableAmount);
            outputPlan->set_amount(0);
            outputPlan->set_change(availableAmount);

            outputPlan->set_to_address(input.gas_change_address());
            outputPlan->set_change_address(input.gas_change_address());
            outputPlan->set_asset_id(input.gas_asset_id());
        }

        if (feeNeed) {
            transactionSize =
                prepareUnsignedTransaction(input, plan, false).serialize().size() + SIGNATURE_SIZE;
            int64_t fee = 0;
            if (transactionSize >= LARGE_TX_SIZE) {
                fee = MIN_FEE_FOR_LARGE_TX;
                fee += transactionSize * FEE_FOR_BYTE_OF_TX;
            }
            fee = input.fee() < fee ? fee : input.fee();
            int64_t change = plan.outputs(existGASTransfer).change() - fee;
            plan.mutable_outputs(existGASTransfer)->set_change(change);
            plan.set_fee(fee);
        }
    } catch (const Common::Proto::SigningError& error) {
        plan.set_error(error);
    }

    return plan;
}

Transaction Signer::prepareUnsignedTransaction(const Proto::SigningInput& input,
                                               const Proto::TransactionPlan& plan, bool validate) {
    try {
        auto transaction = Transaction();
        transaction.type = TransactionType::TT_ContractTransaction;
        transaction.version = 0;

        for (int i = 0; i < plan.inputs_size(); i++) {
            CoinReference coin;
            Data prevHashReverse(plan.inputs(i).prev_hash().begin(),
                                 plan.inputs(i).prev_hash().end());
            std::reverse(prevHashReverse.begin(), prevHashReverse.end());
            coin.prevHash = load(prevHashReverse);
            coin.prevIndex = (uint16_t)plan.inputs(i).prev_index();
            transaction.inInputs.push_back(coin);
        }

        for (int i = 0; i < plan.outputs_size(); i++) {
            if (plan.outputs(i).asset_id() == input.gas_asset_id()) {
                if (validate && plan.outputs(i).amount() + plan.outputs(i).change() + plan.fee() !=
                                    plan.outputs(i).available_amount()) {
                    throw Common::Proto::SigningError(Common::Proto::Error_wrong_fee);
                }
            }

            if (plan.outputs(i).amount() > 0) { // to recipient, 0 if gas change only exist
                TransactionOutput out;
                out.assetId = load(parse_hex(plan.outputs(i).asset_id()));
                out.value = (int64_t)plan.outputs(i).amount();
                auto scriptHash = TW::NEO::Address(plan.outputs(i).to_address()).toScriptHash();
                out.scriptHash = load(scriptHash);
                transaction.outputs.push_back(out);
            }

            // change
            if (plan.outputs(i).change() > 0) {
                TransactionOutput out;
                out.assetId = load(parse_hex(plan.outputs(i).asset_id()));
                out.value = plan.outputs(i).change();
                auto scriptHash = TW::NEO::Address(plan.outputs(i).change_address()).toScriptHash();
                out.scriptHash = load(scriptHash);
                transaction.outputs.push_back(out);
            }
        }
        return transaction;
    } catch (...) {
    }

    return Transaction();
}

Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept {
    auto output = Proto::SigningOutput();
    try {
        auto signer = Signer(PrivateKey(Data(input.private_key().begin(), input.private_key().end())));
        Proto::TransactionPlan plan;
        if (input.has_plan()) {
            plan = input.plan();
        } else {
            plan = signer.plan(input);
        }
        auto transaction = prepareUnsignedTransaction(input, plan);
        signer.sign(transaction);
        auto signedTx = transaction.serialize();

        output.set_encoded(signedTx.data(), signedTx.size());
    } catch (const Common::Proto::SigningError& error) {
        output.set_error(error);
    }

    return output;
}


/*
  All rights reserved.
  Please read that file and understand the license terms
  before using or altering the project.
*/

#include "Consts.hpp"

consts_t::consts_t()

consts_t::~consts_t()
{
	for( auto & t : m_consts ) {
		for( auto & cons : t.second ) {
			VAR_DREF( cons.second );
		}
	}
}

var_base_t * consts_t::get( const std::string & name, const OperTypes type, const int tok_ctr )
{
	if( m_consts[ type ].find( name ) != m_consts[ type ].end() ) {
		if( name == m_consts[ type ][ name ]->to_str() ) {
			return m_consts[ type ][ name ];
		}
		VAR_DREF( m_consts[ type ][ name ] );
	}

	if( type == OP_INT ) return m_consts[ type ][ name ] = new var_int_t( name, tok_ctr );
	else if( type == OP_FLT ) return m_consts[ type ][ name ] = new var_flt_t( name, tok_ctr );
	else if( type == OP_STR || type == OP_CONST ) return m_consts[ type ][ name ] = new var_str_t( name, tok_ctr );

	return nullptr;
}


{
    MetadataElementListPtr elements = mPrivateCollector->FindByLevel(type,
                                                                     level);
    MetadataElementList::const_iterator i;
    for (i = elements->begin(); i != elements->end(); i++)
    {
        MetadataElementPtr element = *i;
        mCollector->AddElement(element);
    }
    mLoadCounter[type] = mLoadCounter[type] + 1;
    mTotalLoadCount++;
}

/*
 *
 * All rights reserved.
 *
 */

#include "testUtil.h"
#include "TestMetadataLoader.h"
#include "librets/DefaultMetadataCollector.h"

using namespace librets;
using namespace librets::util;
using std::string;

#define CLASS TestMetadataLoader

CLASS::CLASS()
    : mPrivateCollector(new DefaultMetadataCollector()), mTotalLoadCount(0)
{
}

void CLASS::SetCollector(MetadataElementCollectorPtr collector)
{
    mCollector = collector;
}

void CLASS::LoadMetadata(MetadataElement::Type type, std::string level)

int CLASS::GetLoadCount(MetadataElement::Type type)
{
    return mLoadCounter[type];
}

int CLASS::GetTotalLoadCount()
{
    return mTotalLoadCount;
}

void CLASS::AddElement(MetadataElementPtr element)
{
    mPrivateCollector->AddElement(element);
}


{
    invariant(!_rollback);
    invariantWTOK(_session->begin_transaction(_session, config));
    _rollback = true;
}

/**
 *
 */

#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kStorage

#include "mongo/platform/basic.h"

#include <cstdio>
#include <fmt/format.h>

#include "mongo/db/storage/wiredtiger/wiredtiger_begin_transaction_block.h"

#include "mongo/db/storage/wiredtiger/wiredtiger_util.h"
#include "mongo/util/errno_util.h"

namespace mongo {
using namespace fmt::literals;

WiredTigerBeginTxnBlock::WiredTigerBeginTxnBlock(
    WT_SESSION* session,
    PrepareConflictBehavior prepareConflictBehavior,
    RoundUpPreparedTimestamps roundUpPreparedTimestamps,
    RoundUpReadTimestamp roundUpReadTimestamp)
    : _session(session) {
    invariant(!_rollback);

    str::stream builder;
    if (prepareConflictBehavior == PrepareConflictBehavior::kIgnoreConflicts) {
        builder << "ignore_prepare=true,";
    } else if (prepareConflictBehavior == PrepareConflictBehavior::kIgnoreConflictsAllowWrites) {
        builder << "ignore_prepare=force,";
    }
    if (roundUpPreparedTimestamps == RoundUpPreparedTimestamps::kRound ||
        roundUpReadTimestamp == RoundUpReadTimestamp::kRound) {
        builder << "roundup_timestamps=(";
        if (roundUpPreparedTimestamps == RoundUpPreparedTimestamps::kRound) {
            builder << "prepared=true,";
        }
        if (roundUpReadTimestamp == RoundUpReadTimestamp::kRound) {
            builder << "read=true";
        }
        builder << "),";
    }
    if (roundUpReadTimestamp == RoundUpReadTimestamp::kNoRoundForce) {
        builder << "read_before_oldest=true,";
    }

    const std::string beginTxnConfigString = builder;
    invariantWTOK(_session->begin_transaction(_session, beginTxnConfigString.c_str()));
    _rollback = true;
}

WiredTigerBeginTxnBlock::WiredTigerBeginTxnBlock(WT_SESSION* session, const char* config)
    : _session(session)

WiredTigerBeginTxnBlock::~WiredTigerBeginTxnBlock() {
    if (_rollback) {
        invariant(_session->rollback_transaction(_session, nullptr) == 0);
    }
}

Status WiredTigerBeginTxnBlock::setReadSnapshot(Timestamp readTimestamp) {
    invariant(_rollback);
    std::string readTSConfigString = "read_timestamp={:x}"_format(readTimestamp.asULL());

    return wtRCToStatus(_session->timestamp_transaction(_session, readTSConfigString.c_str()));
}

void WiredTigerBeginTxnBlock::done() {
    invariant(_rollback);
    _rollback = false;
}

}  // namespace mongo


#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include "vDos.h"
#include "mem.h"
#include "inout.h"
#include "paging.h"
#include "cpu.h"
#include "bios.h"
#include "video.h"

static PageHandler * pageHandlers[64];												// Pagehandlers for first MB

unsigned int TotMemBytes;
unsigned int TotMemMB = 1;
unsigned int TotEXTMB = 0;
unsigned int TotXMSMB = 0;
unsigned int TotEMSMB = 0;
unsigned int EndConvMem = 0x9fff;

HostPt MemBase;

class RAMPageHandler : public PageHandler
	{
public:
	RAMPageHandler()
		{
		flags = PFLAG_READABLE|PFLAG_WRITEABLE;
		}
	Bit8u readb(PhysPt addr)
		{
		return *(MemBase+addr);
		}
	Bit16u readw(PhysPt addr)
		{
		return *(Bit16u*)(MemBase+addr);
		}
	Bit32u readd(PhysPt addr)
		{
		return *(Bit32u*)(MemBase+addr);
		}
	void writeb(PhysPt addr, Bit8u val)
		{
		*(MemBase+addr) = val;
		}
	void writew(PhysPt addr, Bit16u val)
		{
		*(Bit16u*)(MemBase+addr) = val;
		}
	void writed(PhysPt addr, Bit32u val)
		{
		*(Bit32u*)(MemBase+addr) = val;
		}
	HostPt GetHostPt(PhysPt addr)
		{
		return MemBase+addr;
		}
	};

class ROMPageHandler : public RAMPageHandler
	{
public:
	ROMPageHandler()
		{
		flags = PFLAG_READABLE;
		}
	void writeb(PhysPt addr, Bit8u val)
		{
		}
	void writew(PhysPt addr, Bit16u val)
		{
		}
	void writed(PhysPt addr, Bit32u val)
		{
		}
	};

class ILLPageHandler : public RAMPageHandler
	{
public:
	ILLPageHandler()
		{
		flags = 0;
		}
	Bit8u readb(PhysPt addr)
		{
		return 0xff;
		}
	Bit16u readw(PhysPt addr)

	Bit32u readd(PhysPt addr)
		{
		return 0xffffffff;
		}
	void writeb(PhysPt addr, Bit8u val)
		{
		}
	void writew(PhysPt addr, Bit16u val)
		{
		}
	void writed(PhysPt addr, Bit32u val)
		{
		}
	};

static RAMPageHandler ram_page_handler;
static ROMPageHandler rom_page_handler;
static ILLPageHandler ill_page_handler;

#define TLB_SIZE 128															// 128 ebtries in TLB cache (it get cleared most often)
//#define TLB_SIZE 8192															// 8192 ebtries in TLB cache
static Bit32u TLB_phys[TLB_SIZE];
static Bit32u TLB_cache[TLB_SIZE];

void clearTLB(void)
	{
	memset(TLB_cache, 0xff, TLB_SIZE*4);
	}

PhysPt LinToPhys2(LinPt addr)
	{
	int TLB_idx = addr>>12;
	Bit32u pdAddr = (TLB_idx>>8)&0xffc;
	Bit32u pdEntry = *(Bit32u *)(MemBase+PAGING_GetDirBase()+pdAddr);
	if (pdEntry&1)
		{
		Bit32u ptAddr = (pdEntry&~0xfff)+((TLB_idx<<2)&0xffc);
		Bit32u ptEntry = *(Bit32u *)(MemBase+ptAddr);
		if (ptEntry&1)
			{
			TLB_cache[TLB_idx&(TLB_SIZE-1)] = TLB_idx;
			TLB_phys[TLB_idx&(TLB_SIZE-1)] = ptEntry&~0xfff;
			return TLB_phys[TLB_idx&(TLB_SIZE-1)]+(addr&0xfff);
			}
		}
	E_Exit("Page fault: directory or table entry not present: %x -> %03x : %03x : %03x", addr, TLB_idx>>10, TLB_idx&0x3ff, addr&0xfff);
	return 0;																		// To satisfy compiler
	}

__forceinline PhysPt LinToPhys(LinPt addr)
	{
	int TLB_idx = addr>>12;
	if (TLB_cache[TLB_idx&(TLB_SIZE-1)] == TLB_idx)
		return TLB_phys[TLB_idx&(TLB_SIZE-1)]+(addr&0xfff);
	return LinToPhys2(addr);
	}

__forceinline PageHandler * MEM_GetPageHandler(PhysPt addr)
	{
	if (addr < 0x100000)															// If in first MB
		return pageHandlers[addr/MEM_PAGESIZE];
	if (addr < TotMemBytes)															// If in extended (or XMS)
		return &ram_page_handler;
	return &ill_page_handler;
	}

void MEM_SetPageHandler(Bitu phys_page, Bitu pages, PageHandler * handler)
	{
	for (; pages > 0; pages--)
		pageHandlers[phys_page++] = handler;
	}

void MEM_ResetPageHandler(Bitu phys_page, Bitu pages)
	{
	for (; pages > 0; pages--)
		pageHandlers[phys_page++] = &ram_page_handler;
	}

void unhide_vDos()
	{
	if (winHidden && window.hideTill)												// Unhide window on access
		{
		Bit32u tickCount = GetTickCount();
		if (window.hideTill-tickCount < 2300)										// Prevent 4DOS forcing unhide at initialization
			window.hideTill = 0;													// And hide some DOS program intro screens (0.2 sec delay)
		}
	else
		idleCount++;
	}

Bit8u Mem_aLodsb(PhysPt addr)
	{
	return *(Bit8u *)(MemBase+addr);
	}

Bit8u Mem_Lodsb(LinPt addr)
	{
	if (PAGING_Enabled())
		addr = LinToPhys(addr);
	if (addr < 0xa0000)																// If in lower memory, read direct (always readable)
		{
		if ((Bit32u)(addr-BIOS_KEYBOARD_FLAGS1) <= 7)								// Access to keyboard info
			unhide_vDos();
		return *(MemBase+addr);
		}
	if ((addr >= 0xf0000 && addr < TotMemBytes))									// If in last 64KB or extended mem, read direct (always readable)
		return *(MemBase+addr);
	return MEM_GetPageHandler(addr)->readb(addr);
	}

Bit16u Mem_aLodsw(PhysPt addr)
	{
	if ((Bit32u)(addr-BIOS_KEYBOARD_FLAGS1) <= 7)									// Access to keyboard info
		unhide_vDos();
	return *(Bit16u *)(MemBase+addr);
	}

Bit16u Mem_Lodsw(LinPt addr)
	{
	if (PAGING_Enabled())
		addr = LinToPhys(addr);
	if (addr < 0x9ffff)																// If in lower mem, read direct (always readable)
		{
		if ((Bit32u)(addr-BIOS_KEYBOARD_FLAGS1) <= 7)								// Access to keyboard info
			unhide_vDos();
		return *(Bit16u *)(MemBase+addr);
		}
	if ((addr >= 0xf0000 && addr < TotMemBytes-1))									// If in last 64 KB ir extended mem, read direct (always readable)
		return *(Bit16u *)(MemBase+addr);
	if ((addr&(MEM_PAGESIZE-1)) != (MEM_PAGESIZE-1))
		return MEM_GetPageHandler(addr)->readw(addr);
	Bit16u ret = 0;
	for (Bit32u i = addr+1; i >= addr; i--)
		{
		ret <<= 8;
		ret |= MEM_GetPageHandler(i)->readb(i);
		}
	return ret;
	}

Bit32u Mem_aLodsd(PhysPt addr)
	{
	return *(Bit32u *)(MemBase+addr);
	}

Bit32u Mem_Lodsd(LinPt addr)
	{
	if (PAGING_Enabled())
		addr = LinToPhys(addr);
	if (addr < 0x9fffd || (addr >= 0xf0000 && addr < TotMemBytes-3))				// In lower, last 64 KB, or extended mem, read direct (always readable)
		return *(Bit32u *)(MemBase+addr);
	if ((addr&(MEM_PAGESIZE-1)) < (MEM_PAGESIZE-3))
		return MEM_GetPageHandler(addr)->readd(addr);
	Bit32u ret = 0;
	for (Bit32u i = addr+3; i >= addr; i--)
		{
		ret <<= 8;
		ret |= MEM_GetPageHandler(i)->readb(i);
		}
	return ret;
	}

void Mem_Stosb(LinPt addr, Bit8u val)
	{
	if (PAGING_Enabled())
		addr = LinToPhys(addr);

	if (addr < 0xa0000 || (addr > 0xfffff && addr < TotMemBytes))					// If in lower or extended mem, write direct (always writeable)
		{
		*(MemBase+addr) = val;
		return;
		}
	MEM_GetPageHandler(addr)->writeb(addr, val);
	}

void Mem_Stosw(LinPt addr, Bit16u val)
	{
	if (PAGING_Enabled())
		addr = LinToPhys(addr);
	if (addr < 0x9ffff || (addr > 0xfffff && addr < TotMemBytes-1))					// If in lower or extended mem, write direct (always writeable)
		{
		*(Bit16u *)(MemBase+addr) = val;
		return;
		}
	if ((addr&(MEM_PAGESIZE-1)) != (MEM_PAGESIZE-1))
		MEM_GetPageHandler(addr)->writew(addr, val);
	else
		for (int i = 0; i < 2; i++)
			{
			MEM_GetPageHandler(addr)->writeb(addr, val&0xff);
			val >>= 8;
			addr++;
			}
	}

void Mem_Stosd(LinPt addr, Bit32u val)
{
	if (PAGING_Enabled())
		addr = LinToPhys(addr);
	if (addr < 0x9fffd || (addr > 0xfffff && addr < TotMemBytes-3))					// If in lower or extended mem, write direct (always writeable)
		{
		*(Bit32u *)(MemBase+addr) = val;
		return;
		}
	if ((addr&(MEM_PAGESIZE-1)) < (MEM_PAGESIZE-3))
		MEM_GetPageHandler(addr)->writed(addr, val);
	else
		for (int i = 0; i < 4; i++)
			{
			MEM_GetPageHandler(addr)->writeb(addr, (Bit8u)(val&0xff));
			val >>= 8;
			addr++;
			}
	}

void Mem_Movsb(LinPt dest, LinPt src)
	{
	if (PAGING_Enabled())
		{
		src = LinToPhys(src);
		dest = LinToPhys(dest);
		}
	MEM_GetPageHandler(dest)->writeb(dest, MEM_GetPageHandler(src)->readb(src));
	}

void Mem_rMovsb(LinPt dest, LinPt src, Bitu bCount)
	{
	Bit16u maxMove = PAGING_Enabled() ? 4096 : MEM_PAGESIZE;						// If paging, use 4096 bytes page size (strictly not needed?)
	while (bCount)																	// Set in chunks of MEM_PAGESIZE for mem mapping to take effect
		{
		PhysPt physSrc = PAGING_Enabled() ? LinToPhys(src) : src;
		PhysPt physDest = PAGING_Enabled() ? LinToPhys(dest) : dest;
		Bit16u srcOff = ((Bit16u)physSrc)&(maxMove-1);
		Bit16u destOff = ((Bit16u)physDest)&(maxMove-1);
		Bit16u bTodo = maxMove - MAX(srcOff, destOff);
		if (bTodo > bCount)
			bTodo = bCount;
		src += bTodo;
		dest += bTodo;
		bCount -= bTodo;
		PageHandler * phSrc = MEM_GetPageHandler(physSrc);
		PageHandler * phDest = MEM_GetPageHandler(physDest);

		if (phDest->flags&PFLAG_WRITEABLE && phSrc->flags&PFLAG_READABLE)
			{
			Bit8u *hSrc = phSrc->GetHostPt(physSrc);
			Bit8u *hDest = phDest->GetHostPt(physDest);
			if ((hSrc <= hDest && hSrc+bTodo > hDest) || (hDest <= hSrc && hDest+bTodo > hSrc))
				while (bTodo--)														// If source and destination overlap, do it "by hand"
					*(hDest++) = *(hSrc++);											// memcpy() messes things up in another way than rep movsb does!
			else
				memcpy(hDest, hSrc, bTodo);
			}
		else																		// Not writeable, or use (VGA)handler
			while (bTodo--)
				phDest->writeb(physDest++, phSrc->readb(physSrc++));
		}
	}

Bitu Mem_StrLen(LinPt addr)
	{
	for (Bitu len = 0; len < 65536; len++)
		if (!Mem_Lodsb(addr+len))
			return len;
	return 0;																		// This shouldn't happen
	}

void Mem_CopyTo(LinPt dest, void const * const src, Bitu bCount)
	{
	Bit8u const * srcAddr = (Bit8u const *)src;
	Bit16u maxMove = PAGING_Enabled() ? 4096 : MEM_PAGESIZE;						// If paging, use 4096 bytes page size (strictly not needed?)
	while (bCount)
		{
		PhysPt physDest = PAGING_Enabled() ? LinToPhys(dest) : dest;
		Bit16u bTodo = maxMove-(((Bit16u)physDest)&(maxMove-1));
		if (bTodo > bCount)
			bTodo = bCount;
		bCount -= bTodo;
		dest += bTodo;
		PageHandler * ph = MEM_GetPageHandler(physDest);
		if (ph->flags&PFLAG_WRITEABLE)
			{
			memcpy(ph->GetHostPt(physDest), srcAddr, bTodo);
			srcAddr += bTodo;
			}
		else
			while (bTodo--)
				ph->writeb(physDest++, *srcAddr++);
		}
	}

void Mem_CopyFrom(LinPt src, void * dest, Bitu bCount)
	{
	Bit8u * destAddr = (Bit8u *)dest;
	Bit16u maxMove = PAGING_Enabled() ? 4096 : MEM_PAGESIZE;						// If paging, use 4096 bytes page size (strictly not needed?)
	while (bCount)
		{
		PhysPt physSrc = PAGING_Enabled() ? LinToPhys(src) : src;
		Bit16u bTodo = maxMove - (((Bit16u)physSrc)&(maxMove-1));
		if (bTodo > bCount)
			bTodo = bCount;
		bCount -= bTodo;
		src += bTodo;
		PageHandler * ph = MEM_GetPageHandler(physSrc);
		if (ph->flags & PFLAG_READABLE)
			{
			memcpy(destAddr, ph->GetHostPt(physSrc), bTodo);
			destAddr += bTodo;
			}
		else
			while (bTodo--)
				*destAddr++ = ph->readb(physSrc++);
		}
	}

void Mem_StrnCopyFrom(char * data, LinPt pt, Bitu bCount)
	{
	while (bCount--)
		{
		Bit8u c = Mem_Lodsb(pt++);
		if (!c)
			break;
		*data++ = c;
		}
	*data = 0;
	}

void Mem_rStos4b(LinPt addr, Bit32u val32, Bitu bCount)								// Used by rStosw and rStosd (bytes are not the same)
	{
	Bit16u maxMove = PAGING_Enabled() ? 4096 : MEM_PAGESIZE;						// If paging, use 4096 bytes page size (strictly not needed?)
	while (bCount)																	// Set in chunks of MEM_PAGESIZE for mem mapping to take effect
		{
		PhysPt physAddr = PAGING_Enabled() ? LinToPhys(addr) : addr;
		Bit16u bTodo = maxMove - (((Bit16u)physAddr)&(maxMove-1));
		if (bTodo > bCount)
			bTodo = bCount;
		PageHandler *ph = MEM_GetPageHandler(physAddr);
		addr += bTodo;
		if (ph->flags&PFLAG_WRITEABLE)
			{
			HostPt hPtr = ph->GetHostPt(physAddr);
			while ((Bit32u)(hPtr)&3) { // Align start address to 32 bit
				*(hPtr++) = (Bit8u)(val32&0xff);
				val32 = _rotr(val32, 8);
				bTodo--;
				bCount--;
				}
			bCount -= bTodo&~3;
			for (bTodo >>= 2; bTodo; bTodo--) { // Set remaing with 32 bit value
				*(Bit32u *)hPtr = val32;
				hPtr += 4;
				}
				if(bCount < 4) { // Eventually last remaining bytes
				while (bCount--) {
					*hPtr++ = (Bit8u)(val32&0xff);
					val32 >>= 8;
					}
				return;
				}
			}
		else { // Not writeable, or use (VGA)handler
				while ((Bit32u)(physAddr)&3) { // Align start address to 32 bit
				ph->writeb(physAddr++, (Bit8u)(val32&0xff));
				val32 = _rotr(val32, 8);
				bTodo--;
				bCount--;
				}
			bCount -= bTodo&~3;
			for (bTodo >>= 2; bTodo; bTodo--) { // Set remaing with 32 bit value
				ph->writed(physAddr, val32);
				physAddr += 4;
				}
				if (bCount < 4) { // Eventually last remaining bytes
				while (bCount--) {
					ph->writeb(physAddr++, (Bit8u)(val32&0xff));
					val32 >>= 8;
					}
				return;
				}
			}
		}
	}

void Mem_rStosb(LinPt addr, Bit8u val, Bitu count)
	{
	Bit16u maxMove = PAGING_Enabled() ? 4096 : MEM_PAGESIZE;						// If paging, use 4096 bytes page size (strictly not needed?)
	while (count)																	// Set in chunks of MEM_PAGESIZE for mem mapping to take effect
		{
		PhysPt physAddr = PAGING_Enabled() ? LinToPhys(addr) : addr;
		Bit16u bTodo = maxMove-(((Bit16u)physAddr)&(maxMove-1));
		if (bTodo > count)
			bTodo = count;
		count -= bTodo;
		addr += bTodo;
		PageHandler *ph = MEM_GetPageHandler(physAddr);
		if (ph->flags&PFLAG_WRITEABLE)
			memset(ph->GetHostPt(physAddr), val, bTodo);							// memset() is optimized for 32 bit
		else																		// Not writeable, or use (VGA)handler
			while (bTodo--)
				ph->writeb(physAddr++, val);
		}
	}

static void write_p92(Bitu port, Bitu val)
	{
	}

static Bit8u read_p92(Bitu port)
	{
	return 0;
	}

void MEM_Init()
	{
	char *xmem = ConfGetString("xmem");
	if (*xmem)
		{
		int testVal;
		char testStr1[512];
		char testStr2[512];
		bool error = true;
		if (*xmem == '+')															// 704K option
			{
			EndConvMem += 0x1000;
			xmem++;
			}
		if (sscanf(xmem, "%d%s%s", &testVal, testStr1, testStr2) == 2)
			{
			if (testVal > 0 && testVal < 64)
				{
				error = false;
				if (!stricmp("EXT", testStr1))
					TotEXTMB = testVal;
				else if (!stricmp("XMS", testStr1))
					TotXMSMB = testVal;
				else if (!stricmp("EMS", testStr1))
					TotEMSMB = testVal;
				else
					error = true;
				if (!error)
					TotMemMB = TotEXTMB+TotXMSMB+1;
				}
			}
		if (error)
			ConfAddError("Invalid XMEM= parameters\n", xmem);
		}
	else
		{
		TotXMSMB = 4;																// Default 4MB XMS
		TotMemMB = TotXMSMB+1;
		}

	MemBase = (Bit8u *)_aligned_malloc((TotMemMB+TotEMSMB)*1024*1024, MEM_PAGESIZE);// Setup the physical memory
	if (!MemBase)
		E_Exit("Can't allocate main memory of %d MB", TotMemMB+TotEMSMB);
	TotMemBytes = TotMemMB*1024*1024;
	memset((void*)MemBase, 0, TotMemBytes);											// Clear the memory

	for (Bitu i = 0; i < 64; i++)													// Setup handlers for first MB
		pageHandlers[i] = &ram_page_handler;
	for (Bitu i = 0xc0000/MEM_PAGESIZE; i < 0xc4000/MEM_PAGESIZE; i++)				// Setup rom at 0xc0000-0xc3fff
		pageHandlers[i] = &rom_page_handler;
	for (Bitu i = 0xf0000/MEM_PAGESIZE; i < 0x100000/MEM_PAGESIZE; i++)				// Setup rom at 0xf0000-0xfffff
		pageHandlers[i] = &rom_page_handler;

	IO_RegisterWriteHandler(0x92, write_p92);										// (Dummy) A20 Line - PS/2 system control port A
	IO_RegisterReadHandler(0x92, read_p92);
	}


while (bCount)
		{
		PhysPt physDest = PAGING_Enabled() ? LinToPhys(dest) : dest;
		Bit16u bTodo = maxMove-(((Bit16u)physDest)&(maxMove-1));
		if (bTodo > bCount)
			bTodo = bCount;
		bCount -= bTodo;
		dest += bTodo;
		PageHandler * ph = MEM_GetPageHandler(physDest);
		if (ph->flags&PFLAG_WRITEABLE)
			{
			memcpy(ph->GetHostPt(physDest), srcAddr, bTodo);
			srcAddr += bTodo;
			}
		else
			while (bTodo--)
				ph->writeb(physDest++, *srcAddr++);
		}

#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include "vDos.h"
#include "mem.h"
#include "inout.h"
#include "paging.h"
#include "cpu.h"
#include "bios.h"
#include "video.h"

static PageHandler * pageHandlers[64];												// Pagehandlers for first MB

unsigned int TotMemBytes;
unsigned int TotMemMB = 1;
unsigned int TotEXTMB = 0;
unsigned int TotXMSMB = 0;
unsigned int TotEMSMB = 0;
unsigned int EndConvMem = 0x9fff;

HostPt MemBase;

class RAMPageHandler : public PageHandler
	{
public:
	RAMPageHandler()
		{
		flags = PFLAG_READABLE|PFLAG_WRITEABLE;
		}
	Bit8u readb(PhysPt addr)
		{
		return *(MemBase+addr);
		}
	Bit16u readw(PhysPt addr)
		{
		return *(Bit16u*)(MemBase+addr);
		}
	Bit32u readd(PhysPt addr)
		{
		return *(Bit32u*)(MemBase+addr);
		}
	void writeb(PhysPt addr, Bit8u val)
		{
		*(MemBase+addr) = val;
		}
	void writew(PhysPt addr, Bit16u val)
		{
		*(Bit16u*)(MemBase+addr) = val;
		}
	void writed(PhysPt addr, Bit32u val)
		{
		*(Bit32u*)(MemBase+addr) = val;
		}
	HostPt GetHostPt(PhysPt addr)
		{
		return MemBase+addr;
		}
	};

class ROMPageHandler : public RAMPageHandler
	{
public:
	ROMPageHandler()
		{
		flags = PFLAG_READABLE;
		}
	void writeb(PhysPt addr, Bit8u val)
		{
		}
	void writew(PhysPt addr, Bit16u val)
		{
		}
	void writed(PhysPt addr, Bit32u val)
		{
		}
	};

class ILLPageHandler : public RAMPageHandler
	{
public:
	ILLPageHandler()
		{
		flags = 0;
		}
	Bit8u readb(PhysPt addr)
		{
		return 0xff;
		}
	Bit16u readw(PhysPt addr)
		{
		return 0xffff;
		}
	Bit32u readd(PhysPt addr)
		{
		return 0xffffffff;
		}
	void writeb(PhysPt addr, Bit8u val)
		{
		}
	void writew(PhysPt addr, Bit16u val)
		{
		}
	void writed(PhysPt addr, Bit32u val)
		{
		}
	};

static RAMPageHandler ram_page_handler;
static ROMPageHandler rom_page_handler;
static ILLPageHandler ill_page_handler;

#define TLB_SIZE 128															// 128 ebtries in TLB cache (it get cleared most often)
//#define TLB_SIZE 8192															// 8192 ebtries in TLB cache
static Bit32u TLB_phys[TLB_SIZE];
static Bit32u TLB_cache[TLB_SIZE];

void clearTLB(void)
	{
	memset(TLB_cache, 0xff, TLB_SIZE*4);
	}

PhysPt LinToPhys2(LinPt addr)
	{
	int TLB_idx = addr>>12;
	Bit32u pdAddr = (TLB_idx>>8)&0xffc;
	Bit32u pdEntry = *(Bit32u *)(MemBase+PAGING_GetDirBase()+pdAddr);
	if (pdEntry&1)
		{
		Bit32u ptAddr = (pdEntry&~0xfff)+((TLB_idx<<2)&0xffc);
		Bit32u ptEntry = *(Bit32u *)(MemBase+ptAddr);
		if (ptEntry&1)
			{
			TLB_cache[TLB_idx&(TLB_SIZE-1)] = TLB_idx;
			TLB_phys[TLB_idx&(TLB_SIZE-1)] = ptEntry&~0xfff;
			return TLB_phys[TLB_idx&(TLB_SIZE-1)]+(addr&0xfff);
			}
		}
	E_Exit("Page fault: directory or table entry not present: %x -> %03x : %03x : %03x", addr, TLB_idx>>10, TLB_idx&0x3ff, addr&0xfff);
	return 0;																		// To satisfy compiler
	}

__forceinline PhysPt LinToPhys(LinPt addr)
	{
	int TLB_idx = addr>>12;
	if (TLB_cache[TLB_idx&(TLB_SIZE-1)] == TLB_idx)
		return TLB_phys[TLB_idx&(TLB_SIZE-1)]+(addr&0xfff);
	return LinToPhys2(addr);
	}

__forceinline PageHandler * MEM_GetPageHandler(PhysPt addr)
	{
	if (addr < 0x100000)															// If in first MB
		return pageHandlers[addr/MEM_PAGESIZE];
	if (addr < TotMemBytes)															// If in extended (or XMS)
		return &ram_page_handler;
	return &ill_page_handler;
	}

void MEM_SetPageHandler(Bitu phys_page, Bitu pages, PageHandler * handler)
	{
	for (; pages > 0; pages--)
		pageHandlers[phys_page++] = handler;
	}

void MEM_ResetPageHandler(Bitu phys_page, Bitu pages)
	{
	for (; pages > 0; pages--)
		pageHandlers[phys_page++] = &ram_page_handler;
	}

void unhide_vDos()
	{
	if (winHidden && window.hideTill)												// Unhide window on access
		{
		Bit32u tickCount = GetTickCount();
		if (window.hideTill-tickCount < 2300)										// Prevent 4DOS forcing unhide at initialization
			window.hideTill = 0;													// And hide some DOS program intro screens (0.2 sec delay)
		}
	else
		idleCount++;
	}

Bit8u Mem_aLodsb(PhysPt addr)
	{
	return *(Bit8u *)(MemBase+addr);
	}

Bit8u Mem_Lodsb(LinPt addr)
	{
	if (PAGING_Enabled())
		addr = LinToPhys(addr);
	if (addr < 0xa0000)																// If in lower memory, read direct (always readable)
		{
		if ((Bit32u)(addr-BIOS_KEYBOARD_FLAGS1) <= 7)								// Access to keyboard info
			unhide_vDos();
		return *(MemBase+addr);
		}
	if ((addr >= 0xf0000 && addr < TotMemBytes))									// If in last 64KB or extended mem, read direct (always readable)
		return *(MemBase+addr);
	return MEM_GetPageHandler(addr)->readb(addr);
	}

Bit16u Mem_aLodsw(PhysPt addr)
	{
	if ((Bit32u)(addr-BIOS_KEYBOARD_FLAGS1) <= 7)									// Access to keyboard info
		unhide_vDos();
	return *(Bit16u *)(MemBase+addr);
	}

Bit16u Mem_Lodsw(LinPt addr)
	{
	if (PAGING_Enabled())
		addr = LinToPhys(addr);
	if (addr < 0x9ffff)																// If in lower mem, read direct (always readable)
		{
		if ((Bit32u)(addr-BIOS_KEYBOARD_FLAGS1) <= 7)								// Access to keyboard info
			unhide_vDos();
		return *(Bit16u *)(MemBase+addr);
		}
	if ((addr >= 0xf0000 && addr < TotMemBytes-1))									// If in last 64 KB ir extended mem, read direct (always readable)
		return *(Bit16u *)(MemBase+addr);
	if ((addr&(MEM_PAGESIZE-1)) != (MEM_PAGESIZE-1))
		return MEM_GetPageHandler(addr)->readw(addr);
	Bit16u ret = 0;
	for (Bit32u i = addr+1; i >= addr; i--)
		{
		ret <<= 8;
		ret |= MEM_GetPageHandler(i)->readb(i);
		}
	return ret;
	}

Bit32u Mem_aLodsd(PhysPt addr)
	{
	return *(Bit32u *)(MemBase+addr);
	}

Bit32u Mem_Lodsd(LinPt addr)
	{
	if (PAGING_Enabled())
		addr = LinToPhys(addr);
	if (addr < 0x9fffd || (addr >= 0xf0000 && addr < TotMemBytes-3))				// In lower, last 64 KB, or extended mem, read direct (always readable)
		return *(Bit32u *)(MemBase+addr);
	if ((addr&(MEM_PAGESIZE-1)) < (MEM_PAGESIZE-3))
		return MEM_GetPageHandler(addr)->readd(addr);
	Bit32u ret = 0;
	for (Bit32u i = addr+3; i >= addr; i--)
		{
		ret <<= 8;
		ret |= MEM_GetPageHandler(i)->readb(i);
		}
	return ret;
	}

void Mem_Stosb(LinPt addr, Bit8u val)
	{
	if (PAGING_Enabled())
		addr = LinToPhys(addr);

	if (addr < 0xa0000 || (addr > 0xfffff && addr < TotMemBytes))					// If in lower or extended mem, write direct (always writeable)
		{
		*(MemBase+addr) = val;
		return;
		}
	MEM_GetPageHandler(addr)->writeb(addr, val);
	}

void Mem_Stosw(LinPt addr, Bit16u val)
	{
	if (PAGING_Enabled())
		addr = LinToPhys(addr);
	if (addr < 0x9ffff || (addr > 0xfffff && addr < TotMemBytes-1))					// If in lower or extended mem, write direct (always writeable)
		{
		*(Bit16u *)(MemBase+addr) = val;
		return;
		}
	if ((addr&(MEM_PAGESIZE-1)) != (MEM_PAGESIZE-1))
		MEM_GetPageHandler(addr)->writew(addr, val);
	else
		for (int i = 0; i < 2; i++)
			{
			MEM_GetPageHandler(addr)->writeb(addr, val&0xff);
			val >>= 8;
			addr++;
			}
	}

void Mem_Stosd(LinPt addr, Bit32u val)
{
	if (PAGING_Enabled())
		addr = LinToPhys(addr);
	if (addr < 0x9fffd || (addr > 0xfffff && addr < TotMemBytes-3))					// If in lower or extended mem, write direct (always writeable)
		{
		*(Bit32u *)(MemBase+addr) = val;
		return;
		}
	if ((addr&(MEM_PAGESIZE-1)) < (MEM_PAGESIZE-3))
		MEM_GetPageHandler(addr)->writed(addr, val);
	else
		for (int i = 0; i < 4; i++)
			{
			MEM_GetPageHandler(addr)->writeb(addr, (Bit8u)(val&0xff));
			val >>= 8;
			addr++;
			}
	}

void Mem_Movsb(LinPt dest, LinPt src)
	{
	if (PAGING_Enabled())
		{
		src = LinToPhys(src);
		dest = LinToPhys(dest);
		}
	MEM_GetPageHandler(dest)->writeb(dest, MEM_GetPageHandler(src)->readb(src));
	}

void Mem_rMovsb(LinPt dest, LinPt src, Bitu bCount)
	{
	Bit16u maxMove = PAGING_Enabled() ? 4096 : MEM_PAGESIZE;						// If paging, use 4096 bytes page size (strictly not needed?)
	while (bCount)																	// Set in chunks of MEM_PAGESIZE for mem mapping to take effect
		{
		PhysPt physSrc = PAGING_Enabled() ? LinToPhys(src) : src;
		PhysPt physDest = PAGING_Enabled() ? LinToPhys(dest) : dest;
		Bit16u srcOff = ((Bit16u)physSrc)&(maxMove-1);
		Bit16u destOff = ((Bit16u)physDest)&(maxMove-1);
		Bit16u bTodo = maxMove - MAX(srcOff, destOff);
		if (bTodo > bCount)
			bTodo = bCount;
		src += bTodo;
		dest += bTodo;
		bCount -= bTodo;
		PageHandler * phSrc = MEM_GetPageHandler(physSrc);
		PageHandler * phDest = MEM_GetPageHandler(physDest);

		if (phDest->flags&PFLAG_WRITEABLE && phSrc->flags&PFLAG_READABLE)
			{
			Bit8u *hSrc = phSrc->GetHostPt(physSrc);
			Bit8u *hDest = phDest->GetHostPt(physDest);
			if ((hSrc <= hDest && hSrc+bTodo > hDest) || (hDest <= hSrc && hDest+bTodo > hSrc))
				while (bTodo--)														// If source and destination overlap, do it "by hand"
					*(hDest++) = *(hSrc++);											// memcpy() messes things up in another way than rep movsb does!
			else
				memcpy(hDest, hSrc, bTodo);
			}
		else																		// Not writeable, or use (VGA)handler
			while (bTodo--)
				phDest->writeb(physDest++, phSrc->readb(physSrc++));
		}
	}

Bitu Mem_StrLen(LinPt addr)
	{
	for (Bitu len = 0; len < 65536; len++)
		if (!Mem_Lodsb(addr+len))
			return len;
	return 0;																		// This shouldn't happen
	}

void Mem_CopyTo(LinPt dest, void const * const src, Bitu bCount)
	{
	Bit8u const * srcAddr = (Bit8u const *)src;
	Bit16u maxMove = PAGING_Enabled() ? 4096 : MEM_PAGESIZE;						// If paging, use 4096 bytes page size (strictly not needed?)

	}

void Mem_CopyFrom(LinPt src, void * dest, Bitu bCount)
	{
	Bit8u * destAddr = (Bit8u *)dest;
	Bit16u maxMove = PAGING_Enabled() ? 4096 : MEM_PAGESIZE;						// If paging, use 4096 bytes page size (strictly not needed?)
	while (bCount)
		{
		PhysPt physSrc = PAGING_Enabled() ? LinToPhys(src) : src;
		Bit16u bTodo = maxMove - (((Bit16u)physSrc)&(maxMove-1));
		if (bTodo > bCount)
			bTodo = bCount;
		bCount -= bTodo;
		src += bTodo;
		PageHandler * ph = MEM_GetPageHandler(physSrc);
		if (ph->flags & PFLAG_READABLE)
			{
			memcpy(destAddr, ph->GetHostPt(physSrc), bTodo);
			destAddr += bTodo;
			}
		else
			while (bTodo--)
				*destAddr++ = ph->readb(physSrc++);
		}
	}

void Mem_StrnCopyFrom(char * data, LinPt pt, Bitu bCount)
	{
	while (bCount--)
		{
		Bit8u c = Mem_Lodsb(pt++);
		if (!c)
			break;
		*data++ = c;
		}
	*data = 0;
	}

void Mem_rStos4b(LinPt addr, Bit32u val32, Bitu bCount)								// Used by rStosw and rStosd (bytes are not the same)
	{
	Bit16u maxMove = PAGING_Enabled() ? 4096 : MEM_PAGESIZE;						// If paging, use 4096 bytes page size (strictly not needed?)
	while (bCount)																	// Set in chunks of MEM_PAGESIZE for mem mapping to take effect
		{
		PhysPt physAddr = PAGING_Enabled() ? LinToPhys(addr) : addr;
		Bit16u bTodo = maxMove - (((Bit16u)physAddr)&(maxMove-1));
		if (bTodo > bCount)
			bTodo = bCount;
		PageHandler *ph = MEM_GetPageHandler(physAddr);
		addr += bTodo;
		if (ph->flags&PFLAG_WRITEABLE)
			{
			HostPt hPtr = ph->GetHostPt(physAddr);
			while ((Bit32u)(hPtr)&3) { // Align start address to 32 bit
				*(hPtr++) = (Bit8u)(val32&0xff);
				val32 = _rotr(val32, 8);
				bTodo--;
				bCount--;
				}
			bCount -= bTodo&~3;
			for (bTodo >>= 2; bTodo; bTodo--) { // Set remaing with 32 bit value
				*(Bit32u *)hPtr = val32;
				hPtr += 4;
				}
				if(bCount < 4) { // Eventually last remaining bytes
				while (bCount--) {
					*hPtr++ = (Bit8u)(val32&0xff);
					val32 >>= 8;
					}
				return;
				}
			}
		else { // Not writeable, or use (VGA)handler
				while ((Bit32u)(physAddr)&3) { // Align start address to 32 bit
				ph->writeb(physAddr++, (Bit8u)(val32&0xff));
				val32 = _rotr(val32, 8);
				bTodo--;
				bCount--;
				}
			bCount -= bTodo&~3;
			for (bTodo >>= 2; bTodo; bTodo--) { // Set remaing with 32 bit value
				ph->writed(physAddr, val32);
				physAddr += 4;
				}
				if (bCount < 4) { // Eventually last remaining bytes
				while (bCount--) {
					ph->writeb(physAddr++, (Bit8u)(val32&0xff));
					val32 >>= 8;
					}
				return;
				}
			}
		}
	}

void Mem_rStosb(LinPt addr, Bit8u val, Bitu count)
	{
	Bit16u maxMove = PAGING_Enabled() ? 4096 : MEM_PAGESIZE;						// If paging, use 4096 bytes page size (strictly not needed?)
	while (count)																	// Set in chunks of MEM_PAGESIZE for mem mapping to take effect
		{
		PhysPt physAddr = PAGING_Enabled() ? LinToPhys(addr) : addr;
		Bit16u bTodo = maxMove-(((Bit16u)physAddr)&(maxMove-1));
		if (bTodo > count)
			bTodo = count;
		count -= bTodo;
		addr += bTodo;
		PageHandler *ph = MEM_GetPageHandler(physAddr);
		if (ph->flags&PFLAG_WRITEABLE)
			memset(ph->GetHostPt(physAddr), val, bTodo);							// memset() is optimized for 32 bit
		else																		// Not writeable, or use (VGA)handler
			while (bTodo--)
				ph->writeb(physAddr++, val);
		}
	}

static void write_p92(Bitu port, Bitu val)
	{
	}

static Bit8u read_p92(Bitu port)
	{
	return 0;
	}

void MEM_Init()
	{
	char *xmem = ConfGetString("xmem");
	if (*xmem)
		{
		int testVal;
		char testStr1[512];
		char testStr2[512];
		bool error = true;
		if (*xmem == '+')															// 704K option
			{
			EndConvMem += 0x1000;
			xmem++;
			}
		if (sscanf(xmem, "%d%s%s", &testVal, testStr1, testStr2) == 2)
			{
			if (testVal > 0 && testVal < 64)
				{
				error = false;
				if (!stricmp("EXT", testStr1))
					TotEXTMB = testVal;
				else if (!stricmp("XMS", testStr1))
					TotXMSMB = testVal;
				else if (!stricmp("EMS", testStr1))
					TotEMSMB = testVal;
				else
					error = true;
				if (!error)
					TotMemMB = TotEXTMB+TotXMSMB+1;
				}
			}
		if (error)
			ConfAddError("Invalid XMEM= parameters\n", xmem);
		}
	else
		{
		TotXMSMB = 4;																// Default 4MB XMS
		TotMemMB = TotXMSMB+1;
		}

	MemBase = (Bit8u *)_aligned_malloc((TotMemMB+TotEMSMB)*1024*1024, MEM_PAGESIZE);// Setup the physical memory
	if (!MemBase)
		E_Exit("Can't allocate main memory of %d MB", TotMemMB+TotEMSMB);
	TotMemBytes = TotMemMB*1024*1024;
	memset((void*)MemBase, 0, TotMemBytes);											// Clear the memory

	for (Bitu i = 0; i < 64; i++)													// Setup handlers for first MB
		pageHandlers[i] = &ram_page_handler;
	for (Bitu i = 0xc0000/MEM_PAGESIZE; i < 0xc4000/MEM_PAGESIZE; i++)				// Setup rom at 0xc0000-0xc3fff
		pageHandlers[i] = &rom_page_handler;
	for (Bitu i = 0xf0000/MEM_PAGESIZE; i < 0x100000/MEM_PAGESIZE; i++)				// Setup rom at 0xf0000-0xfffff
		pageHandlers[i] = &rom_page_handler;

	IO_RegisterWriteHandler(0x92, write_p92);										// (Dummy) A20 Line - PS/2 system control port A
	IO_RegisterReadHandler(0x92, read_p92);
	}