openDecal/Native/Inject/Attic/Message.cpp
erik d1442e3747 Initial commit: Complete open-source Decal rebuild
All 5 phases of the open-source Decal rebuild:

Phase 1: 14 decompiled .NET projects (Interop.*, Adapter, FileService, DecalUtil)
Phase 2: 10 native DLLs rewritten as C# COM servers with matching GUIDs
  - DecalDat, DHS, SpellFilter, DecalInput, DecalNet, DecalFilters
  - Decal.Core, DecalControls, DecalRender, D3DService
Phase 3: C++ shims for Inject.DLL (D3D9 hooking) and LauncherHook.DLL
Phase 4: DenAgent WinForms tray application
Phase 5: WiX installer and build script

25 C# projects building with 0 errors.
Native C++ projects require VS 2022 + Windows SDK (x86).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 18:27:56 +01:00

345 lines
8.3 KiB
C++

// Message.cpp : Implementation of cMessage
#include "stdafx.h"
#include "Inject.h"
#include "Message.h"
#include "InjectApi.h"
#include "MessageLoaders.h"
#include "MessageParsers.h"
// m_pParent noahb
cMessage::cFieldList::iterator cMessage::cLoadContext::lookupField( cMessage::cMessageElement *pElement )
{
for( cLoadContext *pContext = this; pContext != NULL; pContext = pContext->m_pParent )
{
for( cFieldList::iterator i = m_pMessage->m_fields.begin() + m_dwOffset; i != m_pMessage->m_fields.end(); i += i->m_nOwns )
{
if( i->m_pSchema == pElement )
return i;
}
}
return m_pMessage->m_fields.end();
}
void cMessage::cMessageSchema::loadSchema( DWORD dwSchema )
{
TCHAR szQuery[ 255 ];
::_stprintf( szQuery, _T( "/schema/messages/message[@type='%04X']" ), dwSchema );
MSXML::IXMLDOMElementPtr pMessage = (*g_pXML)->selectSingleNode( szQuery );
if( pMessage.GetInterfacePtr() == NULL )
// Nothing here, so we create a valid but empty message
return;
cElementParser::cContext c( &m_members );
c.parseChildren( pMessage );
}
MSXML::IXMLDOMDocumentPtr * cMessage::g_pXML;
cMessage::cMessageSchemaMap cMessage::g_schema;
void cMessage::init()
{
// Load the schema
TCHAR szPath[ MAX_PATH ];
::InjectMapPath( eInjectPathAgent, _T( "messages.xml" ), szPath );
g_pXML = new MSXML::IXMLDOMDocumentPtr;
(*g_pXML).CreateInstance( __uuidof( MSXML::DOMDocument ) );
(*g_pXML)->load( szPath );
// Initialize our schema helper objects
cFieldLoader::init();
cElementParser::init();
#ifdef _DEBUG
// Load all of the message type
USES_CONVERSION;
MSXML::IXMLDOMNodeListPtr pAllMessages = (*g_pXML)->selectNodes( _T( "/schema/messages/message" ) );
for( MSXML::IXMLDOMElementPtr pMessage = pAllMessages->nextNode(); pMessage.GetInterfacePtr() != NULL; pMessage = pAllMessages->nextNode() )
{
_variant_t vType = pMessage->getAttribute( _T( "type" ) );
if( vType.vt != VT_BSTR )
{
_ASSERTE( FALSE );
continue;
}
// Get the ID from the message
long nID;
if( ::_stscanf( OLE2T( vType.bstrVal ), _T( "%X" ), &nID ) != 1 )
{
_ASSERTE( FALSE );
continue;
}
cMessageSchema ms;
ms.loadSchema( nID );
}
#endif
}
void cMessage::term()
{
g_schema.clear();
cElementParser::term();
cFieldLoader::term();
if( (*g_pXML).GetInterfacePtr() != NULL )
{
(*g_pXML).Release();
delete g_pXML;
}
}
/////////////////////////////////////////////////////////////////////////////
// cMessage
cMessage::cMessage()
: m_nType( 0 ),
m_pStartCrack( NULL ),
m_pEndCrack( NULL ),
m_pEndData( NULL ),
m_pSchema( NULL )
{
}
cMessage::cFieldList::iterator cMessage::getFieldFromElement( cMessageElement *pElement )
{
for( cFieldList::iterator i = m_fields.begin(); i != m_fields.end(); i += i->m_nOwns )
{
cMessageElement *pSchema = i->m_pSchema;
if( pSchema == pElement )
break;
}
return i;
}
void cMessage::crackMessage( BYTE *pBody, DWORD dwSize )
{
m_pStartCrack = pBody;
m_pEndData = pBody + dwSize + sizeof( DWORD );
m_nType = *reinterpret_cast< long * >( pBody );
m_fields.clear();
m_pEndCrack = m_pStartCrack + sizeof( long );
// First look up the message to see if it's already decoded
cMessageSchemaMap::iterator i_schema = g_schema.find( m_nType );
if( i_schema == g_schema.end() )
{
// Make a new one
m_pSchema = new cMessageSchema;
m_pSchema->loadSchema( m_nType );
g_schema.insert( cMessageSchemaMap::value_type( m_nType, std::auto_ptr< cMessageSchema >( m_pSchema ) ) );
}
else
m_pSchema = i_schema->second.get();
// At this point we have "a" schema of some quality
// set up the cursors
// Crack all fields right awway - testing mostly
cLoadContext context( this );
m_icracked = m_pSchema->m_members.begin();
while( m_icracked != m_pSchema->m_members.end() )
{
if( !m_icracked->get()->load( context ) )
{
// Failure to load the data, abort
m_icracked = m_pSchema->m_members.end();
break;
}
// Carry on to the next field
++ m_icracked;
}
}
void cMessage::crackAll()
{
cLoadContext context( this );
while( m_icracked != m_pSchema->m_members.end() )
{
if( !m_icracked->get()->load( context ) )
{
// Failure to load the data, abort
m_icracked = m_pSchema->m_members.end();
break;
}
// Carry on to the next field
++ m_icracked;
}
}
bool cMessage::crackMember( cFieldList::iterator i )
{
if( i != m_fields.end() )
// We aren't at the end yet
return true;
if( m_icracked == m_pSchema->m_members.end() )
// At the end now
return false;
// Attempt to crack the next field
cLoadContext context( this );
while( m_icracked != m_pSchema->m_members.end() )
{
if( !m_icracked->get()->load( context ) )
{
// Failure to load the data, abort
m_icracked = m_pSchema->m_members.end();
break;
}
// Carry on to the next field
++ m_icracked;
if( i != m_fields.end() )
return true;
}
return false;
}
STDMETHODIMP cMessage::get_Type(long *pVal)
{
_ASSERTE( pVal != NULL );
*pVal = *reinterpret_cast< long * >( m_pStartCrack );
return S_OK;
}
STDMETHODIMP cMessage::get_Data(VARIANT *pVal)
{
long nSize = m_pEndData - m_pStartCrack;
if( nSize == 0 )
{
// Special case, this message is entirely cracked - return NULL
pVal->vt = VT_NULL;
pVal->intVal = 0;
return S_OK;
}
// We've got some data to share
SAFEARRAYBOUND sab = { nSize, 0 };
SAFEARRAY *pArray = ::SafeArrayCreate( VT_UI1, 1, &sab );
::SafeArrayAllocData( pArray );
LPVOID pvData;
::SafeArrayAccessData( pArray, &pvData );
::memcpy( pvData, m_pStartCrack, nSize );
::SafeArrayUnaccessData( pArray );
pVal->vt = VT_ARRAY | VT_UI1;
pVal->parray = pArray;
return S_OK;
}
STDMETHODIMP cMessage::get_Member(VARIANT vName, VARIANT *pVal)
{
::VariantInit (pVal);
if( vName.vt == VT_BSTR )
{
_bstr_t bstrName = vName;
// Iterate over the fields and return our match - in this loop we'll do incremental
// cracking, so it's a little messy. When we hit the end, we look to see if there are
// more uncracked fields
int nFieldCount = m_fields.size();
for( int nField = 0; nField != nFieldCount; nField += m_fields[ nField ].m_nOwns )
{
if( m_fields[ nField ].m_pSchema->m_strName == bstrName )
{
m_fields[ nField ].m_pSchema->getValue( this, m_fields.begin() + nField, pVal );
return S_OK;
}
}
pVal->vt = VT_EMPTY;
return S_OK;
}
// Attempt to convert it into an index
HRESULT hRes = ::VariantChangeType( &vName, &vName, 0, VT_I4 );
if( FAILED( hRes ) )
{
_ASSERTE( FALSE );
return hRes;
}
// Check if the value is in range
long nIndex = vName.lVal;
if( nIndex < 0 )
{
_ASSERTE( nIndex >= 0 );
return E_INVALIDARG;
}
// Now, one problem is we aren't exactly sure how big the array is, so we have to walk
// through the fields, skipping appropriately
for( cFieldList::iterator i = m_fields.begin(); crackMember( i ); i += i->m_nOwns, -- nIndex )
{
if( nIndex == 0 )
{
// We've found the index - extract the value
i->m_pSchema->getValue( this, i, pVal );
return S_OK;
}
}
// The index was too high
_ASSERTE( FALSE );
return E_INVALIDARG;
}
STDMETHODIMP cMessage::get_MemberName(long nIndex, BSTR *pVal)
{
_ASSERTE( nIndex >= 0 );
_ASSERTE( pVal != NULL );
USES_CONVERSION;
for( cFieldList::iterator i = m_fields.begin(); crackMember( i ); i += i->m_nOwns, -- nIndex )
{
if( nIndex == 0 )
{
// We've found the index - extract the value
*pVal = OLE2BSTR( i->m_pSchema->m_strName );
return S_OK;
}
}
// The index was too high
return E_INVALIDARG;
}
STDMETHODIMP cMessage::get_Count(long *pVal)
{
_ASSERTE( pVal != NULL );
// We must crack them all to get a count
crackAll();
*pVal = 0;
for( cFieldList::iterator i = m_fields.begin(); i != m_fields.end(); i += i->m_nOwns, ++ ( *pVal ) );
return S_OK;
}