openDecal/Native/PlainText/ScriptPlugin.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

305 lines
No EOL
8 KiB
C++

// ScriptPlugin.cpp : Implementation of cScriptPlugin
#include "stdafx.h"
#include "PlainText.h"
#include "ScriptPlugin.h"
#include "../Inject/Inject.h"
#include "ScriptView.h"
/////////////////////////////////////////////////////////////////////////////
// cScriptPlugin
void cScriptPlugin::getCLSID( BSTR *pbstrCLSID )
{
MSXML::IXMLDOMAttributePtr pAttr = m_pPlugin->selectSingleNode( _T( "/plugin/@clsid" ) );
_ASSERTE( pAttr.GetInterfacePtr() != NULL );
*pbstrCLSID = OLE2BSTR( pAttr->value.bstrVal );
}
void cScriptPlugin::getLanguage( BSTR *pbstrLanguage )
{
MSXML::IXMLDOMAttributePtr pAttr = m_pPlugin->selectSingleNode( _T( "/plugin/@language" ) );
if( pAttr.GetInterfacePtr() == NULL )
*pbstrLanguage = T2BSTR( _T( "VBScript" ) );
else
*pbstrLanguage = OLE2BSTR( pAttr->value.bstrVal );
}
HRESULT cScriptPlugin::LoadScript(BSTR strFilename)
{
USES_CONVERSION;
HRESULT hRes = ::CoCreateInstance( __uuidof( MSXML::DOMDocument ), NULL, CLSCTX_INPROC_SERVER,
__uuidof( MSXML::IXMLDOMDocument ), reinterpret_cast< void ** >( &m_pPlugin ) );
if( FAILED( hRes ) )
return hRes;
bool bLoad = !!m_pPlugin->load( strFilename );
if( !bLoad )
{
// The document failed to load, get the error info for posterity
MSXML::IXMLDOMParseErrorPtr pErr = m_pPlugin->parseError;
long nCode = pErr->errorCode;
long nFilePos = pErr->filepos;
long nLine = pErr->line;
long nLinePos = pErr->linepos;
_bstr_t strReason = pErr->reason;
_bstr_t strText = pErr->srcText;
TCHAR szError[ 1024 ];
::_stprintf( szError, _T( "0x%08X (%i, %i): %s" ),
nCode, nLine, nLinePos, OLE2T( strReason ) );
::MessageBox( NULL, szError, _T( "XML Parse Error" ), MB_ICONERROR | MB_OK );
// Give the user a chance to break and look at this lovely info
_ASSERTE( FALSE );
return E_FAIL;
}
// Looking good
return S_OK;
}
STDMETHODIMP cScriptPlugin::CreateInstance(IDecalEnum *pInitData, REFIID iid, LPVOID *pObject)
{
CComVariant vFile;
HRESULT hRes = pInitData->get_Property( _bstr_t( "File" ), &vFile );
if( FAILED( hRes ) )
return hRes;
if( vFile.vt != VT_BSTR )
return E_FAIL;
m_strFilename = vFile.bstrVal;
return static_cast< IDecalSurrogate * >( this )->QueryInterface( iid, pObject );
}
STDMETHODIMP cScriptPlugin::Initialize( IPluginSite2 *pSite )
{
USES_CONVERSION;
m_pSite = pSite;
// First, map the map and load the script file
CComPtr< IDecal > pDecal;
m_pSite->get_Decal( &pDecal );
CComBSTR strTruePath;
HRESULT hRes = pDecal->MapPath( m_strFilename, &strTruePath );
if( FAILED( hRes ) )
return hRes;
hRes = LoadScript( strTruePath );
if( FAILED( hRes ) )
return hRes;
// Set up the scripting environment, the default scripting language
// is VBScript
CComBSTR strScript;
getLanguage( &strScript );
hRes = createScriptEngine( strScript );
_ASSERTE( SUCCEEDED( hRes ) );
// Load the script text
CComPtr< IActiveScriptParse > pParse;
m_pScript->QueryInterface( &pParse );
pParse->InitNew();
addNamedItem( _bstr_t( _T( "Site" ) ), static_cast< IDispatch * >( this ) );
MSXML::IXMLDOMElementPtr pScript = m_pPlugin->selectSingleNode( _T( "/plugin/script" ) );
// Load the main script block
if( pScript.GetInterfacePtr() != NULL )
{
EXCEPINFO ei;
HRESULT hRes = pParse->ParseScriptText( pScript->text, NULL, NULL, NULL, 0, 0,
SCRIPTITEM_ISVISIBLE, NULL, &ei );
_ASSERTE( SUCCEEDED( hRes ) );
} else {
::MessageBox(NULL, "Couldn't load the main script block", "cScriptPlugin::Initialize", MB_OK);
}
// Walk through the intrinsic object
MSXML::IXMLDOMNodeListPtr pNetworks = m_pPlugin->selectNodes( _T( "/plugin/decal" ) );
for( MSXML::IXMLDOMElementPtr pNetwork = pNetworks->nextNode(); pNetwork.GetInterfacePtr() != NULL; pNetwork = pNetworks->nextNode() )
{
CComPtr< IDispatch > pObject;
HRESULT hRes = pDecal->get_Object( pNetwork->getAttribute( _T( "path" ) ).bstrVal, IID_IDispatch, reinterpret_cast< void ** >( &pObject ) );
if( FAILED( hRes ) )
{
::MessageBox(NULL, "SCRIPT ERROR: Couldn't load decal object", OLE2A(pNetwork->getAttribute(_T("path")).bstrVal), MB_OK);
continue;
}
// Add the item
addNamedItem( pNetwork->getAttribute( _T( "name" ) ).bstrVal, pObject );
}
// Walk through the instance objects
MSXML::IXMLDOMNodeListPtr pObjects = m_pPlugin->selectNodes( _T( "/plugin/object" ) );
for( MSXML::IXMLDOMElementPtr pObject = pObjects->nextNode(); pObject.GetInterfacePtr() != NULL; pObject = pObjects->nextNode() )
{
_variant_t vProgid = pObject->getAttribute( _T( "progid" ) );
if( vProgid.vt == VT_NULL )
{
_ASSERT( FALSE );
continue;
}
CComPtr< IDispatch > pInstance;
CLSID clsid;
HRESULT hRes = ::CLSIDFromProgID( vProgid.bstrVal, &clsid );
if( FAILED( hRes ) )
{
_ASSERT( FALSE );
continue;
}
hRes = ::CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER, IID_IDispatch, reinterpret_cast< void ** >( &pInstance ) );
if( FAILED( hRes ) )
{
_ASSERT( FALSE );
continue;
}
// Add the item
addNamedItem( pObject->getAttribute( _T( "name" ) ).bstrVal, pInstance );
}
// Start up the plugin
m_pScript->SetScriptState(SCRIPTSTATE_CONNECTED);
Fire_Initialize();
return S_OK;
}
STDMETHODIMP cScriptPlugin::Terminate()
{
// Shut down the plugin
Fire_Terminate();
m_pScript->SetScriptState( SCRIPTSTATE_DISCONNECTED );
m_pScript->Close();
m_pSite.Release();
m_pPlugin.Release();
return S_OK;
}
STDMETHODIMP cScriptPlugin::CreateObject( BSTR strProgID, LPDISPATCH *pDisp )
{
if( pDisp == NULL )
{
_ASSERT( FALSE );
return E_POINTER;
}
HRESULT hRes;
CLSID clsid;
if( strProgID[ 0 ] == OLESTR( '{' ) )
hRes = ::CLSIDFromString( strProgID, &clsid );
else
hRes = ::CLSIDFromProgID( strProgID, &clsid );
if( FAILED( hRes ) )
return hRes;
return ::CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER, IID_IDispatch, reinterpret_cast< void ** >( pDisp ) );
}
STDMETHODIMP cScriptPlugin::WriteToChatWindow(BSTR szText, long lColor)
{
getPluginSite()->WriteToChatWindow(szText, lColor);
return S_OK;
}
STDMETHODIMP cScriptPlugin::MessageBox(BSTR Title, BSTR Text)
{
USES_CONVERSION;
::MessageBox(NULL, OLE2A(Text), OLE2A(Title), MB_OK);
return S_OK;
}
IPluginSite * cScriptPlugin::getPluginSite()
{
CComPtr< IDecal > pDecal;
m_pSite->get_Decal ( &pDecal );
CComPtr< IInjectService > pInject;
CComPtr< IPluginSite > pSite;
HRESULT hRes = pDecal->get_Object ( _bstr_t ( _T( "services\\DecalPlugins.InjectService" ) ),
__uuidof ( IInjectService ), reinterpret_cast< LPVOID * > ( &pInject ) );
pInject->get_Site(&pSite);
return pSite;
}
STDMETHODIMP cScriptPlugin::LoadView(BSTR Text, IView **pView)
{
getPluginSite()->LoadView(Text, pView);
return S_OK;
}
void cScriptPlugin::removeView( CScriptView *pView )
{
for( cViewList::iterator i = m_views.begin(); i != m_views.end(); ++ i )
{
if( *i == pView )
{
m_views.erase( i );
return;
}
}
// The view was not found in the list
_ASSERTE( FALSE );
}
void cScriptPlugin::postRemoveView( CScriptView *pView )
{
m_destroy.push_back( pView );
}
STDMETHODIMP cScriptPlugin::CreateView(BSTR strTemplateName, LPDISPATCH *ppNewView)
{
USES_CONVERSION;
TCHAR szQuery[ 255 ];
::_stprintf( szQuery, _T( "/plugin/view[@name='%s']" ), OLE2T( strTemplateName ) );
MSXML::IXMLDOMElementPtr pView = m_pPlugin->selectSingleNode( szQuery );
if( pView.GetInterfacePtr() == NULL )
return E_INVALIDARG;
CComObject< CScriptView > *pNewView;
CComObject< CScriptView >::CreateInstance( &pNewView );
CComPtr< IDispatch > pDispPlugin;
m_pScript->GetScriptDispatch( NULL, &pDispPlugin );
CComBSTR strLanguage;
getLanguage( &strLanguage );
pNewView->loadView( this, pView, ppNewView );
return S_OK;
}