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>
This commit is contained in:
commit
d1442e3747
1382 changed files with 170725 additions and 0 deletions
393
Native/DecalNet/Message.cpp
Normal file
393
Native/DecalNet/Message.cpp
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
// Message.cpp : Implementation of cMessage
|
||||
#include "stdafx.h"
|
||||
#include "DecalNet.h"
|
||||
#include "NetService.h"
|
||||
#include "Message.h"
|
||||
|
||||
#include "MessageLoaders.h"
|
||||
#include "MessageParsers.h"
|
||||
#include "MessageRoot.h"
|
||||
|
||||
cMessage::cFieldList::iterator cMessage::cLoadContext::lookupField( cMessage::cMessageElement *pElement )
|
||||
{
|
||||
for( cLoadContext *pContext = this; pContext != NULL; pContext = pContext->m_pParent )
|
||||
{
|
||||
for( cFieldList::iterator i = pContext->m_pMessage->m_fields.begin() + pContext->m_dwOffset; i != pContext->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( MSXML::IXMLDOMDocumentPtr &pDoc, DWORD dwSchema )
|
||||
{
|
||||
TCHAR szQuery[ 255 ];
|
||||
::_stprintf( szQuery, _T( "/schema/messages/message[@type='%04X']" ), dwSchema );
|
||||
|
||||
MSXML::IXMLDOMElementPtr pMessage = pDoc->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 );
|
||||
}
|
||||
|
||||
void cMessage::init()
|
||||
{
|
||||
// Load the schema
|
||||
CComBSTR strTemplate( _T( "%decal%\\messages.xml" ) ),
|
||||
strPath;
|
||||
m_pService->m_pDecal->MapPath( strTemplate, &strPath );
|
||||
|
||||
g_pXML.CreateInstance( __uuidof( MSXML::DOMDocument ) );
|
||||
BOOL bSuccess = g_pXML->load( strPath.m_str );
|
||||
|
||||
if( ! bSuccess )
|
||||
{
|
||||
USES_CONVERSION;
|
||||
std::string szXML;
|
||||
DecryptXML( OLE2A( strPath.m_str ), szXML );
|
||||
|
||||
if( szXML != "" )
|
||||
bSuccess = g_pXML->loadXML( _bstr_t( szXML.c_str() ) );
|
||||
}
|
||||
|
||||
// Initialize our schema helper objects
|
||||
cFieldLoader::init();
|
||||
cElementParser::init();
|
||||
}
|
||||
|
||||
void cMessage::DecryptXML( const char *szPath, std::string &szXML )
|
||||
{
|
||||
if( szPath == NULL )
|
||||
{
|
||||
szXML = "";
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *f = fopen( szPath, "rb" );
|
||||
if( f == NULL )
|
||||
{
|
||||
szXML = "";
|
||||
return;
|
||||
}
|
||||
|
||||
szXML.clear();
|
||||
unsigned char szBuffer[1025];
|
||||
|
||||
try
|
||||
{
|
||||
CCryptProv crypt;
|
||||
if( crypt.Initialize( PROV_RSA_FULL, "Decal_Memlocs", MS_DEF_PROV ) == NTE_BAD_KEYSET )
|
||||
crypt.Initialize( PROV_RSA_FULL, "Decal_Memlocs", MS_DEF_PROV, CRYPT_NEWKEYSET );
|
||||
|
||||
CCryptMD5Hash hash;
|
||||
hash.Initialize( crypt );
|
||||
hash.AddString( DECAL_KEY );
|
||||
|
||||
CCryptDerivedKey key;
|
||||
key.Initialize( crypt, hash );
|
||||
|
||||
DWORD dwDecLen = 0;
|
||||
|
||||
while( ! feof(f) )
|
||||
{
|
||||
memset( szBuffer, 0, sizeof( szBuffer ) );
|
||||
dwDecLen = fread( szBuffer, 1, 1024, f );
|
||||
key.Decrypt( feof(f), (BYTE *) szBuffer, &dwDecLen );
|
||||
szXML += (char *)szBuffer;
|
||||
}
|
||||
|
||||
key.Destroy();
|
||||
hash.Destroy();
|
||||
crypt.Release();
|
||||
}
|
||||
|
||||
catch( ... )
|
||||
{
|
||||
// crap...
|
||||
szXML = "";
|
||||
}
|
||||
|
||||
fclose( f );
|
||||
}
|
||||
|
||||
void cMessage::term()
|
||||
{
|
||||
g_schema.clear();
|
||||
cElementParser::term();
|
||||
cFieldLoader::term();
|
||||
|
||||
if( g_pXML.GetInterfacePtr() != NULL )
|
||||
g_pXML.Release();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// cMessage
|
||||
|
||||
cMessage::cMessage()
|
||||
: m_nType( 0 ),
|
||||
m_pStartCrack( NULL ),
|
||||
m_pEndCrack( NULL ),
|
||||
m_pEndData( NULL ),
|
||||
m_pSchema( NULL ),
|
||||
m_pRoot( 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 );
|
||||
m_pSchema = NULL;
|
||||
}
|
||||
|
||||
bool cMessage::loadNextElement()
|
||||
{
|
||||
if( m_iLoaded == m_pSchema->m_members.end() )
|
||||
return false;
|
||||
|
||||
// Attempt to load what we've got
|
||||
cLoadContext context( this );
|
||||
if( !m_iLoaded->get()->load( context ) )
|
||||
{
|
||||
m_iLoaded = m_pSchema->m_members.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
++ m_iLoaded;
|
||||
return true;
|
||||
}
|
||||
|
||||
void cMessage::loadAllElements()
|
||||
{
|
||||
if( m_pSchema == NULL )
|
||||
{
|
||||
// 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( g_pXML, m_nType );
|
||||
g_schema.insert( cMessageSchemaMap::value_type( m_nType, VSBridge::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
|
||||
m_iLoaded = m_pSchema->m_members.begin();
|
||||
}
|
||||
|
||||
if( m_iLoaded == m_pSchema->m_members.end() )
|
||||
return;
|
||||
|
||||
cLoadContext context( this );
|
||||
while ( m_iLoaded != m_pSchema->m_members.end () )
|
||||
{
|
||||
if ( m_iLoaded->get()->load ( context ) )
|
||||
++ m_iLoaded;
|
||||
else
|
||||
m_iLoaded = m_pSchema->m_members.end();
|
||||
}
|
||||
}
|
||||
|
||||
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 - sizeof( DWORD );
|
||||
|
||||
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_Begin(IMessageIterator **pVal)
|
||||
{
|
||||
if( m_pRoot == NULL )
|
||||
{
|
||||
// Create a new message root object
|
||||
CComObject< cMessageRoot > *pRoot;
|
||||
CComObject< cMessageRoot >::CreateInstance( &pRoot );
|
||||
|
||||
// Do an extra addref so this object sticks around after the client is done with it
|
||||
pRoot->AddRef();
|
||||
pRoot->init( this );
|
||||
|
||||
m_pRoot = pRoot;
|
||||
}
|
||||
|
||||
m_pRoot->Reset();
|
||||
|
||||
if( m_pSchema == NULL )
|
||||
{
|
||||
// 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( g_pXML, m_nType );
|
||||
g_schema.insert( cMessageSchemaMap::value_type( m_nType, VSBridge::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
|
||||
m_iLoaded = m_pSchema->m_members.begin();
|
||||
}
|
||||
|
||||
return m_pRoot->QueryInterface( IID_IMessageIterator, reinterpret_cast< void ** >( pVal ) );
|
||||
}
|
||||
|
||||
STDMETHODIMP cMessage::get_Member(VARIANT vName, VARIANT *pVal)
|
||||
{
|
||||
loadAllElements ();
|
||||
|
||||
::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(); i != m_fields.end(); 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)
|
||||
{
|
||||
loadAllElements ();
|
||||
|
||||
_ASSERTE( nIndex >= 0 );
|
||||
_ASSERTE( pVal != NULL );
|
||||
|
||||
USES_CONVERSION;
|
||||
|
||||
for( cFieldList::iterator i = m_fields.begin(); i != m_fields.end(); 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)
|
||||
{
|
||||
loadAllElements ();
|
||||
|
||||
_ASSERTE( pVal != NULL );
|
||||
|
||||
*pVal = 0;
|
||||
for( cFieldList::iterator i = m_fields.begin(); i != m_fields.end(); i += i->m_nOwns, ++ ( *pVal ) );
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue