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>
351 lines
7.5 KiB
C++
351 lines
7.5 KiB
C++
// InputBuffer.cpp : Implementation of cInputBuffer
|
|
#include "stdafx.h"
|
|
#include "DecalInput.h"
|
|
#include "InputBuffer.h"
|
|
|
|
#include "InputService.h"
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// cInputBuffer
|
|
|
|
void cInputBuffer::FinalRelease()
|
|
{
|
|
if( cInputService::g_p != NULL && cInputService::g_p->m_pActive == this )
|
|
Stop();
|
|
}
|
|
|
|
bool cInputBuffer::checkState()
|
|
{
|
|
if( cInputService::g_p == NULL || cInputService::g_p->m_pActive == this )
|
|
{
|
|
// There must be an input service and it must be inactive
|
|
_ASSERT( FALSE );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
HRESULT cInputBuffer::addItem( BSTR strInit, IInputAction **ppAction )
|
|
{
|
|
USES_CONVERSION;
|
|
LPTSTR szType = OLE2T( strInit );
|
|
|
|
size_t iInitData = ::_tcscspn( szType, _T( ":" ) );
|
|
|
|
if( iInitData == -1 )
|
|
return E_INVALIDARG;
|
|
|
|
szType[ iInitData ] = _T( '\0' );
|
|
LPCTSTR szInitData = szType + iInitData + 1;
|
|
::_tcslwr( szType );
|
|
|
|
for( cInputService::cActionTypeList::iterator i = cInputService::g_p->m_actiontypes.begin(); i != cInputService::g_p->m_actiontypes.end(); ++ i )
|
|
{
|
|
if( ::_tcscmp( szType, i->szAction ) == 0 )
|
|
break;
|
|
}
|
|
|
|
if( i == cInputService::g_p->m_actiontypes.end() )
|
|
return E_INVALIDARG;
|
|
|
|
// Get the configuration object for this inputaction
|
|
static _bstr_t _strActions( _T( "InputActions" ) );
|
|
CComPtr< IDecalEnum > pInputEnum;
|
|
|
|
if( cInputService::g_p->m_pDecal->get_Configuration( _strActions, i->m_clsid, &pInputEnum ) != S_OK )
|
|
// What used to be there is gone
|
|
return E_FAIL;
|
|
|
|
HRESULT hRes = pInputEnum->CreateInstance( __uuidof( IInputAction ), reinterpret_cast< void ** >( ppAction ) );
|
|
if( FAILED( hRes ) )
|
|
// Could not create our object
|
|
return hRes;
|
|
|
|
return ( *ppAction )->Initialize( this, _bstr_t( szInitData ) );
|
|
}
|
|
|
|
void cInputBuffer::runActions()
|
|
{
|
|
m_bWaiting = false;
|
|
|
|
while( m_play != m_entries.end() )
|
|
{
|
|
HRESULT hRes;
|
|
switch( m_play->m_eType )
|
|
{
|
|
case eExecute:
|
|
hRes = m_play->m_pAction->Execute();
|
|
break;
|
|
|
|
case ePush:
|
|
m_playstack.push( &*m_play );
|
|
hRes = m_play->m_pAction->Push();
|
|
break;
|
|
|
|
case ePop:
|
|
m_playstack.pop();
|
|
hRes = m_play->m_pAction->Pop();
|
|
break;
|
|
|
|
default:
|
|
hRes = E_FAIL;
|
|
}
|
|
|
|
if( FAILED( hRes ) )
|
|
{
|
|
_ASSERT( FALSE );
|
|
Stop();
|
|
return;
|
|
}
|
|
|
|
if( m_bWaiting || cInputService::g_p->m_pActive != this )
|
|
// If we're now waiting, return from our actions and continue
|
|
return;
|
|
|
|
++ m_play;
|
|
}
|
|
|
|
// We're done here
|
|
Stop();
|
|
}
|
|
|
|
STDMETHODIMP cInputBuffer::get_Tag(VARIANT *pVal)
|
|
{
|
|
if( pVal == NULL )
|
|
{
|
|
_ASSERT( FALSE );
|
|
return E_POINTER;
|
|
}
|
|
|
|
return ::VariantCopy( pVal, &m_tag );
|
|
}
|
|
|
|
STDMETHODIMP cInputBuffer::put_Tag(VARIANT newVal)
|
|
{
|
|
m_tag = newVal;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP cInputBuffer::Add(BSTR strCommand)
|
|
{
|
|
if( !checkState() )
|
|
return E_FAIL;
|
|
|
|
cEntry e;
|
|
HRESULT hRes = addItem( strCommand, &e.m_pAction );
|
|
if( FAILED( hRes ) )
|
|
return hRes;
|
|
|
|
e.m_eType = eExecute;
|
|
m_entries.push_back( e );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP cInputBuffer::Push(BSTR Command)
|
|
{
|
|
if( !checkState() )
|
|
return E_FAIL;
|
|
|
|
cEntry e;
|
|
HRESULT hRes = addItem( Command, &e.m_pAction );
|
|
if( FAILED( hRes ) )
|
|
return hRes;
|
|
|
|
VARIANT_BOOL bStackable;
|
|
e.m_pAction->get_Stackable( &bStackable );
|
|
|
|
if( !bStackable )
|
|
return E_INVALIDARG;
|
|
|
|
e.m_eType = ePush;
|
|
m_entries.push_back( e );
|
|
m_insertstack.push( &m_entries.back() );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP cInputBuffer::Pop()
|
|
{
|
|
if( !checkState() )
|
|
return E_FAIL;
|
|
|
|
if( m_insertstack.size() == 0 )
|
|
{
|
|
_ASSERT( FALSE );
|
|
return E_FAIL;
|
|
}
|
|
|
|
cEntry e;
|
|
e.m_eType = ePop;
|
|
e.m_pAction = m_insertstack.top()->m_pAction;
|
|
|
|
m_entries.push_back( e );
|
|
m_insertstack.pop();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP cInputBuffer::get_CanRun(VARIANT_BOOL *pVal)
|
|
{
|
|
if( pVal == NULL )
|
|
{
|
|
_ASSERT( FALSE );
|
|
return E_POINTER;
|
|
}
|
|
|
|
if( cInputService::g_p == NULL )
|
|
{
|
|
// There must be an input service
|
|
_ASSERT( FALSE );
|
|
return E_FAIL;
|
|
}
|
|
|
|
*pVal = ( cInputService::g_p->m_pActive == NULL ) ? VARIANT_TRUE : VARIANT_FALSE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP cInputBuffer::Run()
|
|
{
|
|
if( cInputService::g_p == NULL || cInputService::g_p->m_pActive != NULL )
|
|
{
|
|
// There must be an input service and it must be inactive
|
|
_ASSERT( FALSE );
|
|
return E_FAIL;
|
|
}
|
|
|
|
// First check if the insert stack is incomplete
|
|
while( m_insertstack.size() > 0 )
|
|
Pop();
|
|
|
|
// Reset the cursors
|
|
while( m_playstack.size() > 0 )
|
|
m_playstack.pop();
|
|
|
|
m_play = m_entries.begin();
|
|
cInputService::g_p->m_pActive = this;
|
|
|
|
// Reset the actions
|
|
for( cEntryList::iterator i = m_entries.begin(); i != m_entries.end(); ++ i )
|
|
i->m_pAction->Reset();
|
|
|
|
Fire_Begin( this );
|
|
|
|
// Lock down the user input
|
|
::BlockInput( TRUE );
|
|
runActions();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP cInputBuffer::Stop()
|
|
{
|
|
if( cInputService::g_p == NULL )
|
|
{
|
|
// There must be an input service
|
|
_ASSERT( FALSE );
|
|
return E_FAIL;
|
|
}
|
|
|
|
if( cInputService::g_p->m_pActive == NULL )
|
|
return S_FALSE;
|
|
|
|
if( cInputService::g_p->m_pActive != this )
|
|
return E_FAIL;
|
|
|
|
// Stop immediately, flush all entries in the stack
|
|
while( m_playstack.size() > 0 )
|
|
{
|
|
m_playstack.top()->m_pAction->Pop();
|
|
m_playstack.pop();
|
|
}
|
|
|
|
cInputService::g_p->m_pActive = NULL;
|
|
::BlockInput( FALSE );
|
|
|
|
Fire_End( this );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP cInputBuffer::Delay(long Time, VARIANT_BOOL Advance)
|
|
{
|
|
if( Advance )
|
|
++ m_play;
|
|
|
|
m_bWaiting = true;
|
|
m_nUntilTime = ::timeGetTime() + Time;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP cInputBuffer::FireEvent(long nEventID, VARIANT vParam)
|
|
{
|
|
Fire_Event( this, nEventID, vParam );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP cInputBuffer::MoveMouse(long X, long Y)
|
|
{
|
|
if( cInputService::g_p == NULL )
|
|
{
|
|
// There must be an input service
|
|
_ASSERT( FALSE );
|
|
return E_FAIL;
|
|
}
|
|
|
|
// These are already traslated to screen coords
|
|
cInputService *pis = cInputService::g_p;
|
|
if( pis->m_eWindows == cInputService::eWindows98NoMemLocs )
|
|
return E_FAIL;
|
|
|
|
// Otherwise, get the window proc and send some info to move the mouse
|
|
WNDPROC wndProc = reinterpret_cast< WNDPROC >( ::GetWindowLong( pis->m_hWndHook, GWL_WNDPROC ) );
|
|
POINT ptScreen = { X, Y };
|
|
::ClientToScreen( pis->m_hWndHook, &ptScreen );
|
|
|
|
::SetCursorPos( ptScreen.x, ptScreen.y );
|
|
POINTS pts = { X, Y };
|
|
wndProc( pis->m_hWndHook, WM_MOUSEMOVE, 0, *reinterpret_cast< DWORD * >( &pts ) );
|
|
|
|
// Special additional functionality for win98
|
|
if( pis->m_eWindows == cInputService::eWindows98WithMemLocs )
|
|
{
|
|
pis->m_pnOffset1[ 0 ] = X;
|
|
pis->m_pnOffset1[ 1 ] = Y;
|
|
|
|
pis->m_pnOffset2[ 0 ] = X;
|
|
pis->m_pnOffset2[ 1 ] = Y;
|
|
pis->m_pnOffset2[ 2 ] = X;
|
|
pis->m_pnOffset2[ 3 ] = Y;
|
|
pis->m_pnOffset2[ 4 ] = X;
|
|
pis->m_pnOffset2[ 5 ] = Y;
|
|
|
|
// Delay a couple frames to be sure it's taken effect
|
|
Delay( 100, VARIANT_TRUE );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP cInputBuffer::get_Service(IInputService **pVal)
|
|
{
|
|
if( pVal == NULL )
|
|
{
|
|
_ASSERT( FALSE );
|
|
return E_POINTER;
|
|
}
|
|
|
|
if( cInputService::g_p == NULL )
|
|
{
|
|
// There must be an input service
|
|
_ASSERT( FALSE );
|
|
return E_FAIL;
|
|
}
|
|
|
|
return static_cast< IInputService * >( cInputService::g_p )->QueryInterface( pVal );
|
|
}
|