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:
erik 2026-02-08 18:27:56 +01:00
commit d1442e3747
1382 changed files with 170725 additions and 0 deletions

View file

@ -0,0 +1,351 @@
// 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 );
}