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
530
Native/Inject/InputBuffer.cpp
Normal file
530
Native/Inject/InputBuffer.cpp
Normal file
|
|
@ -0,0 +1,530 @@
|
|||
// InputBuffer.cpp : Implementation of cInputBuffer
|
||||
#include "stdafx.h"
|
||||
#include "Inject.h"
|
||||
#include "InputBuffer.h"
|
||||
|
||||
#include "Manager.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// cInputBuffer
|
||||
|
||||
struct cCharNames
|
||||
{
|
||||
LPCTSTR szName;
|
||||
WORD m_nVKey;
|
||||
};
|
||||
|
||||
static cCharNames _charnames[] = {
|
||||
{ _T( "BACKSPACE" ), VK_BACK },
|
||||
{ _T( "BS" ), VK_BACK },
|
||||
{ _T( "BKSP" ), VK_BACK },
|
||||
{ _T( "CAPSLOCK" ), VK_CAPITAL },
|
||||
{ _T( "DELETE" ), VK_DELETE },
|
||||
{ _T( "DEL" ), VK_DELETE },
|
||||
{ _T( "DOWN" ), VK_DOWN },
|
||||
{ _T( "END" ), VK_END },
|
||||
{ _T( "ENTER" ), VK_RETURN },
|
||||
{ _T( "ESC" ), VK_ESCAPE },
|
||||
{ _T( "HELP" ), VK_HELP },
|
||||
{ _T( "HOME" ), VK_HOME },
|
||||
{ _T( "INS" ), VK_INSERT },
|
||||
{ _T( "INSERT" ), VK_INSERT },
|
||||
{ _T( "LEFT" ), VK_LEFT },
|
||||
{ _T( "NUMLOCK" ), VK_NUMLOCK },
|
||||
{ _T( "PGDN" ), VK_NEXT },
|
||||
{ _T( "PGUP" ), VK_PRIOR },
|
||||
{ _T( "PRTSC" ), VK_SNAPSHOT },
|
||||
{ _T( "RIGHT" ), VK_RIGHT },
|
||||
{ _T( "SCROLLLOCK" ), VK_SCROLL },
|
||||
{ _T( "TAB" ), VK_TAB },
|
||||
{ _T( "UP" ), VK_UP },
|
||||
{ _T( "F1" ), VK_F1 },
|
||||
{ _T( "F2" ), VK_F2 },
|
||||
{ _T( "F3" ), VK_F3 },
|
||||
{ _T( "F4" ), VK_F4 },
|
||||
{ _T( "F5" ), VK_F5 },
|
||||
{ _T( "F6" ), VK_F6 },
|
||||
{ _T( "F7" ), VK_F7 },
|
||||
{ _T( "F8" ), VK_F8 },
|
||||
{ _T( "F9" ), VK_F9 },
|
||||
{ _T( "F10" ), VK_F10 },
|
||||
{ _T( "F11" ), VK_F11 },
|
||||
{ _T( "F12" ), VK_F12 },
|
||||
{ _T( "F13" ), VK_F13 },
|
||||
{ _T( "F14" ), VK_F14 },
|
||||
{ _T( "F15" ), VK_F15 },
|
||||
{ _T( "F16" ), VK_F16 },
|
||||
{ _T( "+" ), VK_ADD } },
|
||||
*_end_charnames = _charnames + ( sizeof(_charnames ) / sizeof( cCharNames ) );
|
||||
|
||||
#define MASK_SHIFT 0x0100
|
||||
#define MASK_CTRL 0x0200
|
||||
#define MASK_ALT 0x0400
|
||||
|
||||
#define SHIFT_MASK 0x0700
|
||||
#define VKEY_MASK 0x00FF
|
||||
|
||||
void pushVirtualKey( WORD vk, cInputVec &vec, bool bDown )
|
||||
{
|
||||
INPUT i;
|
||||
::memset( &i, 0, sizeof( INPUT ) );
|
||||
i.type = INPUT_KEYBOARD;
|
||||
i.ki.wVk = vk;
|
||||
i.ki.wScan = MapVirtualKey( vk, 0 );
|
||||
i.ki.dwFlags = ( bDown ) ? 0 : KEYEVENTF_KEYUP;
|
||||
|
||||
vec.push_back( i );
|
||||
}
|
||||
|
||||
void processKeyboardChars( BSTR strChars, cInputVec &vec )
|
||||
{
|
||||
USES_CONVERSION;
|
||||
|
||||
typedef std::stack< WORD > cMaskStack;
|
||||
cMaskStack mask;
|
||||
|
||||
WORD wCurrentMask = 0;
|
||||
|
||||
std::string strParse = OLE2T( strChars );
|
||||
|
||||
for( std::string::iterator i = strParse.begin(); i != strParse.end(); )
|
||||
{
|
||||
switch( *i )
|
||||
{
|
||||
case _T( '+' ):
|
||||
_ASSERTE( ( i + 2 ) < strParse.end() );
|
||||
_ASSERTE( *( i + 1 ) == _T( '(' ) );
|
||||
_ASSERTE( !( wCurrentMask & MASK_SHIFT ) );
|
||||
|
||||
wCurrentMask |= MASK_SHIFT;
|
||||
mask.push( MASK_SHIFT );
|
||||
|
||||
// Send the keydown
|
||||
pushVirtualKey( VK_SHIFT, vec, true );
|
||||
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case _T( '^' ):
|
||||
_ASSERTE( ( i + 2 ) < strParse.end() );
|
||||
_ASSERTE( *( i + 1 ) == _T( '(' ) );
|
||||
_ASSERTE( !( wCurrentMask & MASK_CTRL ) );
|
||||
|
||||
wCurrentMask |= MASK_CTRL;
|
||||
mask.push( MASK_CTRL );
|
||||
|
||||
// Send the keydown
|
||||
pushVirtualKey( VK_CONTROL, vec, true );
|
||||
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case _T( '%' ):
|
||||
_ASSERTE( ( i + 2 ) < strParse.end() );
|
||||
_ASSERTE( *( i + 1 ) == _T( '(' ) );
|
||||
_ASSERTE( !( wCurrentMask & MASK_ALT ) );
|
||||
|
||||
wCurrentMask |= MASK_ALT;
|
||||
mask.push( MASK_ALT );
|
||||
|
||||
// Send the keydown
|
||||
pushVirtualKey( VK_MENU, vec, true );
|
||||
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case _T( ')' ):
|
||||
_ASSERTE( mask.size() > 0 );
|
||||
|
||||
{
|
||||
DWORD wMask = mask.top();
|
||||
mask.pop();
|
||||
|
||||
switch( wMask )
|
||||
{
|
||||
case MASK_SHIFT:
|
||||
pushVirtualKey( VK_SHIFT, vec, false );
|
||||
break;
|
||||
|
||||
case MASK_CTRL:
|
||||
pushVirtualKey( VK_CONTROL, vec, false );
|
||||
break;
|
||||
|
||||
case MASK_ALT:
|
||||
pushVirtualKey( VK_MENU, vec, false );
|
||||
break;
|
||||
|
||||
default:
|
||||
// Bad value in the stack
|
||||
_ASSERTE( FALSE );
|
||||
break;
|
||||
}
|
||||
|
||||
wCurrentMask &= ~wMask;
|
||||
}
|
||||
|
||||
++ i;
|
||||
break;
|
||||
|
||||
case _T( '{' ):
|
||||
// This is a special key
|
||||
++ i;
|
||||
{
|
||||
for( std::string::iterator j = i; *j != _T( '}' ); ++ j )
|
||||
{
|
||||
_ASSERTE( j != strParse.end() );
|
||||
}
|
||||
|
||||
std::string strSpecial( i, j );
|
||||
for( cCharNames *i_name = _charnames; i_name != _end_charnames; ++ i_name )
|
||||
{
|
||||
if( strSpecial.compare( i_name->szName ) == 0 )
|
||||
{
|
||||
// We found the match, push and release the vkey
|
||||
pushVirtualKey( i_name->m_nVKey, vec, true );
|
||||
pushVirtualKey( i_name->m_nVKey, vec, false );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The special key was not found in the list
|
||||
_ASSERTE( i_name != _end_charnames );
|
||||
|
||||
i = j + 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Assume everything else is a text character but may require additional
|
||||
// shifting
|
||||
{
|
||||
WORD wVkScan = VkKeyScan( *i ),
|
||||
wAdditionalShift = ( wVkScan & SHIFT_MASK ) & ~wCurrentMask,
|
||||
wVKey = wVkScan & VKEY_MASK;
|
||||
|
||||
if( wAdditionalShift & MASK_SHIFT )
|
||||
pushVirtualKey( VK_SHIFT, vec, true );
|
||||
if( wAdditionalShift & MASK_CTRL )
|
||||
pushVirtualKey( VK_CONTROL, vec, true );
|
||||
if( wAdditionalShift & MASK_ALT )
|
||||
pushVirtualKey( VK_MENU, vec, true );
|
||||
|
||||
pushVirtualKey( wVKey, vec, true );
|
||||
pushVirtualKey( wVKey, vec, false );
|
||||
|
||||
if( wAdditionalShift & MASK_ALT )
|
||||
pushVirtualKey( VK_MENU, vec, false );
|
||||
if( wAdditionalShift & MASK_CTRL )
|
||||
pushVirtualKey( VK_CONTROL, vec, false );
|
||||
if( wAdditionalShift & MASK_SHIFT )
|
||||
pushVirtualKey( VK_SHIFT, vec, false );
|
||||
}
|
||||
|
||||
++ i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we released all shifts
|
||||
_ASSERTE( wCurrentMask == 0 );
|
||||
}
|
||||
|
||||
void pushMouseEvent( DWORD dwFlags, cInputVec &vec )
|
||||
{
|
||||
INPUT i;
|
||||
::memset( &i, 0, sizeof( INPUT ) );
|
||||
i.type = INPUT_MOUSE;
|
||||
i.mi.dwFlags = dwFlags;
|
||||
|
||||
vec.push_back( i );
|
||||
}
|
||||
|
||||
void processMouseInput( eMouseInput eType, cInputVec &vec )
|
||||
{
|
||||
DWORD dwError = ::GetLastError();
|
||||
|
||||
switch( eType )
|
||||
{
|
||||
case eMouseLeftDoubleClick:
|
||||
pushMouseEvent( MOUSEEVENTF_LEFTDOWN, vec );
|
||||
pushMouseEvent( MOUSEEVENTF_LEFTUP, vec );
|
||||
case eMouseLeftClick:
|
||||
pushMouseEvent( MOUSEEVENTF_LEFTDOWN, vec );
|
||||
pushMouseEvent( MOUSEEVENTF_LEFTUP, vec );
|
||||
break;
|
||||
|
||||
case eMouseRightDoubleClick:
|
||||
pushMouseEvent( MOUSEEVENTF_RIGHTDOWN, vec );
|
||||
pushMouseEvent( MOUSEEVENTF_RIGHTUP, vec );
|
||||
case eMouseRightClick:
|
||||
pushMouseEvent( MOUSEEVENTF_RIGHTDOWN, vec );
|
||||
pushMouseEvent( MOUSEEVENTF_RIGHTUP, vec );
|
||||
break;
|
||||
|
||||
default:
|
||||
_ASSERTE( FALSE );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE cInputBuffer::m_hThread = NULL;
|
||||
HANDLE cInputBuffer::m_hInputWaiting = NULL;
|
||||
HANDLE cInputBuffer::m_hTerm = NULL;
|
||||
|
||||
CRITICAL_SECTION cInputBuffer::m_csQueue;
|
||||
cInputBuffer::cInputQueue cInputBuffer::m_queue;
|
||||
|
||||
DWORD WINAPI inputThreadProc( LPVOID pvParam );
|
||||
|
||||
void cInputBuffer::init()
|
||||
{
|
||||
::InitializeCriticalSection( &m_csQueue );
|
||||
m_hTerm = ::CreateEvent( NULL, TRUE, FALSE, NULL );
|
||||
m_hInputWaiting = ::CreateEvent( NULL, TRUE, FALSE, NULL );
|
||||
|
||||
DWORD dwThreadID;
|
||||
m_hThread = ::CreateThread( NULL, 0, inputThreadProc, NULL, 0, &dwThreadID );
|
||||
}
|
||||
|
||||
void cInputBuffer::term()
|
||||
{
|
||||
// Clear out the queue
|
||||
::EnterCriticalSection( &m_csQueue );
|
||||
while( m_queue.size() > 0 )
|
||||
m_queue.pop_front();
|
||||
::SetEvent( m_hTerm );
|
||||
::LeaveCriticalSection( &m_csQueue );
|
||||
|
||||
::WaitForSingleObject( m_hThread, INFINITE );
|
||||
|
||||
::CloseHandle( m_hThread );
|
||||
::CloseHandle( m_hInputWaiting );
|
||||
::CloseHandle( m_hTerm );
|
||||
::DeleteCriticalSection( &m_csQueue );
|
||||
}
|
||||
|
||||
void cInputBuffer::postBuffer()
|
||||
{
|
||||
::EnterCriticalSection( &m_csQueue );
|
||||
if( m_queue.empty() )
|
||||
::SetEvent( m_hInputWaiting );
|
||||
|
||||
// Marshal our interface into a drop in in the queue
|
||||
IStream *pStream;
|
||||
::CoMarshalInterThreadInterfaceInStream( IID_IInputNotify, static_cast< IInputNotify * >( this ), &pStream );
|
||||
m_queue.push_back( cInputQueue::value_type( this, pStream ) );
|
||||
|
||||
m_status = eInputWaiting;
|
||||
|
||||
::LeaveCriticalSection( &m_csQueue );
|
||||
}
|
||||
|
||||
cInputVec *cInputBuffer::getLastInput()
|
||||
{
|
||||
if( m_actions.size() == 0 || m_actions.back().first != eActionInput )
|
||||
// Add in a new buffer
|
||||
m_actions.push_back( cActionList::value_type( eActionInput, VSBridge::auto_ptr< cAction >( new cInput ) ) );
|
||||
|
||||
// Return the last buffer
|
||||
return &( static_cast< cInput * >( m_actions.back().second.get() )->m_input );
|
||||
}
|
||||
|
||||
DWORD WINAPI inputThreadProc( LPVOID pvParam )
|
||||
{
|
||||
// This thread must join the mult-threaded apartment becasue it does
|
||||
// not create a message loop
|
||||
::CoInitializeEx( NULL, COINIT_MULTITHREADED );
|
||||
|
||||
BYTE prevKeyboard[ 256 ],
|
||||
blankKeyboard[ 256 ];
|
||||
|
||||
::memset( blankKeyboard, 0, sizeof( blankKeyboard ) );
|
||||
|
||||
HANDLE hEvent[2] = { cInputBuffer::m_hInputWaiting, cInputBuffer::m_hTerm };
|
||||
|
||||
while( ::WaitForMultipleObjects( 2, hEvent, FALSE, INFINITE ) == WAIT_OBJECT_0 )
|
||||
{
|
||||
// Extract the next item from the stream
|
||||
::EnterCriticalSection( &cInputBuffer::m_csQueue );
|
||||
|
||||
cInputBuffer *pBuffer = cInputBuffer::m_queue.front().first;
|
||||
CComPtr< IInputNotify > pNotify;
|
||||
::CoGetInterfaceAndReleaseStream( cInputBuffer::m_queue.front().second, IID_IInputNotify, reinterpret_cast< void ** >( &pNotify ) );
|
||||
|
||||
cInputBuffer::m_queue.pop_front();
|
||||
|
||||
if( cInputBuffer::m_queue.empty() )
|
||||
::ResetEvent( cInputBuffer::m_hInputWaiting );
|
||||
|
||||
::LeaveCriticalSection( &cInputBuffer::m_csQueue );
|
||||
|
||||
// Save the keyboard state and take control of the input queue
|
||||
::BlockInput( TRUE );
|
||||
::GetKeyboardState( prevKeyboard );
|
||||
::SetKeyboardState( blankKeyboard );
|
||||
|
||||
// Looking good, process the messages
|
||||
pNotify->NotifyBegin();
|
||||
|
||||
// The delay list contains the breaks in the input, so we use the delay list as a master
|
||||
// and use segments of the input queue
|
||||
for( cInputBuffer::cActionList::iterator i = pBuffer->m_actions.begin(); i != pBuffer->m_actions.end(); ++ i )
|
||||
i->second->execute( pNotify );
|
||||
|
||||
// Restore keyboard state
|
||||
::SetKeyboardState( prevKeyboard );
|
||||
pNotify->NotifyEnd();
|
||||
|
||||
::BlockInput( FALSE );
|
||||
}
|
||||
|
||||
::CoUninitialize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The action execution functions
|
||||
void cInputBuffer::cInput::execute( IInputNotify * )
|
||||
{
|
||||
UINT nSent = SendInput( m_input.size(), &(*(m_input.begin())), sizeof( INPUT ) );
|
||||
DWORD dwError = GetLastError();
|
||||
_ASSERTE( nSent == m_input.size() );
|
||||
}
|
||||
|
||||
void cInputBuffer::cDelay::execute( IInputNotify *pNotify )
|
||||
{
|
||||
if( m_bAllowInput )
|
||||
{
|
||||
BlockInput( FALSE );
|
||||
pNotify->NotifyPause();
|
||||
}
|
||||
|
||||
::Sleep( m_nMillis );
|
||||
|
||||
if( m_bAllowInput )
|
||||
{
|
||||
BlockInput( TRUE );
|
||||
pNotify->NotifyBegin();
|
||||
}
|
||||
}
|
||||
|
||||
void cInputBuffer::cMouseMove::execute( IInputNotify *pib )
|
||||
{
|
||||
pib->SetMousePos( m_ptClient.x, m_ptClient.y );
|
||||
}
|
||||
|
||||
STDMETHODIMP cInputBuffer::Clear()
|
||||
{
|
||||
if( m_status != eInputIdle )
|
||||
{
|
||||
_ASSERTE( FALSE );
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
m_actions.clear();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP cInputBuffer::TypeText(BSTR strText)
|
||||
{
|
||||
if( m_status != eInputIdle )
|
||||
{
|
||||
_ASSERTE( FALSE );
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
processKeyboardChars( strText, *getLastInput() );
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP cInputBuffer::Delay(long nMilliseconds, VARIANT_BOOL bAllowInput )
|
||||
{
|
||||
if( m_status != eInputIdle )
|
||||
{
|
||||
_ASSERTE( FALSE );
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
cDelay *pDelay = new cDelay;
|
||||
pDelay->m_bAllowInput = !!bAllowInput;
|
||||
pDelay->m_nMillis = nMilliseconds;
|
||||
|
||||
m_actions.push_back( cActionList::value_type( eActionDelay, VSBridge::auto_ptr< cAction >( pDelay ) ) );
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP cInputBuffer::MouseClick(long nX, long nY, eMouseInput eAction)
|
||||
{
|
||||
if( m_status != eInputIdle )
|
||||
{
|
||||
_ASSERTE( FALSE );
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// First insert a mouse movement
|
||||
cMouseMove *pMM = new cMouseMove;
|
||||
pMM->m_ptClient.x = nX;
|
||||
pMM->m_ptClient.y = nY;
|
||||
|
||||
m_actions.push_back( cActionList::value_type( eActionMouseMove, VSBridge::auto_ptr< cAction >( pMM ) ) );
|
||||
processMouseInput( eAction, *getLastInput() );
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP cInputBuffer::get_Status(eInputStatus *pVal)
|
||||
{
|
||||
_ASSERTE( pVal != NULL );
|
||||
|
||||
*pVal = m_status;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP cInputBuffer::Run()
|
||||
{
|
||||
postBuffer();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP cInputBuffer::NotifyBegin()
|
||||
{
|
||||
m_status = eInputRunning;
|
||||
|
||||
Fire_Begin();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP cInputBuffer::NotifyEnd()
|
||||
{
|
||||
m_status = eInputIdle;
|
||||
|
||||
Fire_End();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP cInputBuffer::SetMousePos(long nX, long nY)
|
||||
{
|
||||
// Convert to screen coords
|
||||
POINT pt = { nX, nY };
|
||||
::ClientToScreen( cManager::_p->m_hMain, &pt );
|
||||
::SetCursorPos( pt.x, pt.y );
|
||||
|
||||
// Force the mouse message on the into the window proc
|
||||
// This updates AC to be the new message location
|
||||
cManager::_p->m_pfnOld( cManager::_p->m_hMain, WM_MOUSEMOVE, 0, MAKELONG( nX, nY ) );
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP cInputBuffer::NotifyPause()
|
||||
{
|
||||
Fire_Pause();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue