openDecal/Native/Decal/DecalEnum.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

519 lines
12 KiB
C++

// DecalEnum.cpp : Implementation of cDecalEnum
#include "stdafx.h"
#include "Decal.h"
#include "DecalEnum.h"
/////////////////////////////////////////////////////////////////////////////
// cDecalEnum
cDecalEnum::~cDecalEnum()
{
// Write out the order if there were changes
if ( !m_bOrderChanged )
return;
USES_CONVERSION;
std::string strOrder;
strOrder.reserve ( m_keys.size () );
for ( cKeyList::iterator i = m_keys.begin (); i != m_keys.end(); ++ i )
strOrder += i->m_letter;
// Compile the new order from the
RegKey hkGroup;
if( hkGroup.Create( HKEY_LOCAL_MACHINE, m_strGroupKey.c_str() ) != ERROR_SUCCESS )
{
_ASSERTE ( FALSE );
return;
}
hkGroup.SetStringValue (_T("Order"), strOrder.c_str ());
}
bool cDecalEnum::Initialize( IDecal *pDecal, BSTR strKey )
{
_ASSERTE( pDecal != NULL );
USES_CONVERSION;
m_strGroupKey = "SOFTWARE\\Decal\\";
m_strGroupKey += OLE2A( strKey );
m_strGroup = strKey;
m_pDecal = pDecal;
m_nIndex = -1;
m_bOrderChanged = false;
return true;
}
HRESULT cDecalEnum::Advance( REFCLSID clsid )
{
// Assign a dummy letter
cKeyEntry ke;
ke.m_clsid = clsid;
ke.m_letter = _T( 'A' );
m_keys.reserve ( 1 );
m_keys.push_back ( ke );
return openKeyAt ( 0 );
}
HRESULT cDecalEnum::Begin()
{
USES_CONVERSION;
// Load and sort the list
RegKey hkGroup;
if( hkGroup.Create( HKEY_LOCAL_MACHINE, m_strGroupKey.c_str() ) != ERROR_SUCCESS )
return Error ( IDE_GROUPKEYNOTOPEN );
// Load the order string and prepare a new item string
std::string strIdentifiers( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,./;'[]-=`<>?:{}|_+`!@#$%^&*()" );
DWORD nMaxOrder = strIdentifiers.length ();
LPTSTR szOrder = reinterpret_cast< LPTSTR > ( _alloca ( sizeof ( TCHAR ) * nMaxOrder ) );
// Count the number of subkeys
DWORD dwSubkeys;
if ( ::RegQueryInfoKey ( hkGroup, NULL, NULL, NULL, &dwSubkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) != ERROR_SUCCESS )
return Error ( IDE_INVALIDSUBKEYS );
m_keys.reserve ( dwSubkeys );
LPTSTR szEndOrder;
// Load an existing order
if ( hkGroup.QueryStringValue (_T("Order"), szOrder, &nMaxOrder) == ERROR_SUCCESS )
{
szEndOrder = szOrder + ( nMaxOrder - 1 );
// Sort and merge the differences
std::string strOrder ( szOrder, szEndOrder );
std::sort ( strOrder.begin (), strOrder.end () );
std::string strMerged;
strMerged.reserve ( strIdentifiers.length () - ( nMaxOrder - 1 ) );
std::set_difference ( strIdentifiers.begin (), strIdentifiers.end (), strOrder.begin (), strOrder.end (), std::inserter ( strMerged, strMerged.end() ) );
// Reset the identifiers to exclude the currently used ones
strIdentifiers.swap ( strMerged );
}
else
{
szOrder[ 0 ] = _T( '\0' );
szEndOrder = szOrder;
}
std::string::iterator iNextID = strIdentifiers.begin ();
// Now we walk through the subkeys and insert in order
TCHAR szCLSID[ 64 ];
for ( int i = 0; ::RegEnumKey ( hkGroup, i, szCLSID, sizeof ( szCLSID ) ) == ERROR_SUCCESS; ++ i )
{
CLSID clsid;
HRESULT hRes = ::CLSIDFromString ( T2OLE ( szCLSID ), &clsid );
if ( FAILED ( hRes ) )
// Skip non-CLSID keys
continue;
RegKey hkItem;
if ( hkItem.Open ( hkGroup, szCLSID ) != ERROR_SUCCESS )
continue;
// Attempt to read the sequence indicator
TCHAR szSequence[ 4 ];
DWORD dwSequence = sizeof ( szSequence );
cKeyEntry ke;
ke.m_clsid = clsid;
if ( hkItem.QueryStringValue ( _T( "Order" ), szSequence, &dwSequence ) == ERROR_SUCCESS )
{
_ASSERTE ( szSequence[ 1 ] == _T( '\0' ) );
ke.m_letter = *szSequence;
insertKey ( ke, szOrder, szEndOrder );
continue;
}
// There's no sequence current, add on to the end
_ASSERTE ( iNextID != strIdentifiers.end () );
ke.m_letter = *( iNextID ++ );
m_keys.push_back ( ke );
TCHAR szVal[ 2 ] = { ke.m_letter, _T( '\0' ) };
hkItem.SetStringValue ( _T( "Order" ), szVal );
// The sequence is changed by adding items
m_bOrderChanged = true;
}
return S_OK;
}
void cDecalEnum::insertKey ( cKeyEntry &ke, TCHAR *szOrder, TCHAR *szEndOrder )
{
// Insert the item in the correct order into the list - first
// find the sequence number in the list
for ( TCHAR *szOrderIndex = szOrder; szOrderIndex != szEndOrder; ++ szOrderIndex )
if ( *szOrderIndex == ke.m_letter )
break;
if ( szOrderIndex == szEndOrder )
{
// This item is last, don't search for anything after
m_keys.push_back ( ke );
return;
}
// We have an insert, search for the member to insert before
for ( szOrderIndex = szOrderIndex + 1; szOrderIndex != szEndOrder; ++ szOrderIndex )
{
// Search for a matchinf item in the array
for ( cKeyList::iterator j = m_keys.begin(); j != m_keys.end(); ++ j )
{
if ( j->m_letter == *szOrderIndex )
{
m_keys.insert ( j, ke );
return;
}
}
}
// The next entry most likely hasn't been loaded yet - just append
m_keys.push_back ( ke );
}
HRESULT cDecalEnum::openKeyAt ( int nIndex )
{
USES_CONVERSION;
if ( m_key.m_hKey != NULL )
m_key.Close ();
LPOLESTR strClsid;
::StringFromCLSID( m_keys[ nIndex ].m_clsid, &strClsid );
LPCTSTR szClsid = OLE2T( strClsid );
::CoTaskMemFree( strClsid );
std::string strItemKey = m_strGroupKey + _T( "\\" );
strItemKey += szClsid;
if ( m_key.Open ( HKEY_LOCAL_MACHINE, strItemKey.c_str () ) != ERROR_SUCCESS )
return Error ( IDE_CLASSKEYNOTFOUND );
m_nIndex = nIndex;
return S_OK;
}
STDMETHODIMP cDecalEnum::get_FriendlyName(BSTR *pVal)
{
if( pVal == NULL )
{
_ASSERT( FALSE );
return E_POINTER;
}
if( m_key.m_hKey == NULL )
{
_ASSERT( FALSE );
return Error ( IDE_BADITERATOR );
}
USES_CONVERSION;
TCHAR szName[ MAX_PATH ];
DWORD dwSize = MAX_PATH;
if( m_key.QueryStringValue( NULL, szName, &dwSize ) != ERROR_SUCCESS )
return E_FAIL;
*pVal = T2BSTR( szName );
return S_OK;
}
STDMETHODIMP cDecalEnum::get_ComClass(CLSID *pVal)
{
if( pVal == NULL )
{
_ASSERT( FALSE );
return E_POINTER;
}
if( m_key.m_hKey == NULL )
{
_ASSERT( FALSE );
return Error ( IDE_BADITERATOR );
}
USES_CONVERSION;
*pVal = m_keys[ m_nIndex ].m_clsid;
return S_OK;
}
STDMETHODIMP cDecalEnum::get_Enabled(VARIANT_BOOL *pVal)
{
if( pVal == NULL )
{
_ASSERT( FALSE );
return E_POINTER;
}
if( m_key.m_hKey == NULL )
{
_ASSERT( FALSE );
return Error ( IDE_BADITERATOR );
}
DWORD dwEnabled;
if( m_key.QueryDWORDValue( _T("Enabled"), dwEnabled ) != ERROR_SUCCESS )
// Plugins are enabled when this value is ommitted
dwEnabled = TRUE;
*pVal = ( dwEnabled ) ? VARIANT_TRUE : VARIANT_FALSE;
return S_OK;
}
STDMETHODIMP cDecalEnum::put_Enabled(VARIANT_BOOL newVal)
{
if( m_key.m_hKey == NULL )
{
_ASSERT( FALSE );
return Error ( IDE_BADITERATOR );
}
if( m_key.SetDWORDValue( _T( "Enabled" ), ( ( newVal ) ? 1 : 0 ) ) != ERROR_SUCCESS )
return E_FAIL;
return S_OK;
}
STDMETHODIMP cDecalEnum::CreateInstance(REFIID iid, LPVOID *ppvItf)
{
CLSID clsidSurrogate;
if( FAILED( get_SurrogateClass( &clsidSurrogate ) ) )
{
_ASSERT( FALSE );
return E_FAIL;
}
if( clsidSurrogate == GUID_NULL )
{
// Create the object without a surrogate
CLSID clsidObject;
HRESULT hRes = get_ComClass( &clsidObject );
if( FAILED( hRes ) )
return hRes;
return ::CoCreateInstance( clsidObject, NULL, CLSCTX_INPROC_SERVER, iid, ppvItf );
}
// Create the surrogate object
CComPtr< IDecalEnum > pEnum;
static _bstr_t _strSurrogates ( _T( "Surrogates" ) );
HRESULT hRes = m_pDecal->get_Configuration ( _strSurrogates, clsidSurrogate, &pEnum );
if ( !SUCCEEDED ( hRes ) )
return hRes;
VARIANT_BOOL bEnabled;
hRes = pEnum->get_Enabled ( &bEnabled );
_ASSERTE ( SUCCEEDED ( hRes ) );
if ( !bEnabled )
return Error ( IDE_SURROGATEDISABLED );
CComPtr< IDecalSurrogate > pSurrogate;
hRes = pEnum->CreateInstance ( IID_IDecalSurrogate, reinterpret_cast< LPVOID * > ( &pSurrogate ) );
if( FAILED( hRes ) )
return hRes;
return pSurrogate->CreateInstance( this, iid, ppvItf );
}
STDMETHODIMP cDecalEnum::Next()
{
if ( m_nIndex == m_keys.size () - 1 )
{
// End of iteration
if ( m_key.m_hKey != NULL )
m_key.Close ();
return S_FALSE;
}
return openKeyAt ( m_nIndex + 1 );
}
STDMETHODIMP cDecalEnum::get_SurrogateClass(CLSID *pVal)
{
if( pVal == NULL )
{
_ASSERT( FALSE );
return E_POINTER;
}
if( m_key.m_hKey == NULL )
{
_ASSERT( FALSE );
return Error ( IDE_BADITERATOR );
}
USES_CONVERSION;
TCHAR szName[ MAX_PATH + 1 ];
DWORD dwSize = MAX_PATH + 1;
if( m_key.QueryStringValue( _T("Surrogate"), szName, &dwSize ) != ERROR_SUCCESS )
{
// If the surrogate is not found, return the NULL guid
*pVal = GUID_NULL;
return S_OK;
}
return ::CLSIDFromString( T2OLE( szName ), pVal );
}
STDMETHODIMP cDecalEnum::get_ResourcePath(BSTR *pVal)
{
if( pVal == NULL )
{
_ASSERT( FALSE );
return E_POINTER;
}
if( m_key.m_hKey == NULL )
{
_ASSERT( FALSE );
return Error ( IDE_BADITERATOR );
}
USES_CONVERSION;
TCHAR szName[ MAX_PATH ];
DWORD dwSize = MAX_PATH;
if( m_key.QueryStringValue( NULL, szName, &dwSize ) != ERROR_SUCCESS )
return E_FAIL;
*pVal = T2BSTR( szName );
return S_OK;
}
STDMETHODIMP cDecalEnum::get_Property(BSTR Name, VARIANT *pVal)
{
if( pVal == NULL )
{
_ASSERT( FALSE );
return E_POINTER;
}
if( m_key.m_hKey == NULL )
{
_ASSERT( FALSE );
return Error ( IDE_BADITERATOR );
}
USES_CONVERSION;
LPCTSTR szValueName = OLE2T( Name );
// Get some info about the property
DWORD dwDataType;
if( ::RegQueryValueEx( m_key.m_hKey, szValueName, NULL, &dwDataType, NULL, NULL ) != ERROR_SUCCESS )
// We'll assume there's no value
return E_INVALIDARG;
switch( dwDataType )
{
case REG_DWORD:
{
DWORD dwValue;
if( m_key.QueryDWORDValue( szValueName, dwValue ) != ERROR_SUCCESS )
return E_FAIL;
pVal->vt = VT_I4;
pVal->lVal = dwValue;
return S_OK;
}
break;
case REG_EXPAND_SZ:
case REG_SZ:
{
TCHAR szValue[ MAX_PATH ];
DWORD dwSize = MAX_PATH;
if( m_key.QueryStringValue( szValueName, szValue, &dwSize ) != ERROR_SUCCESS )
return E_FAIL;
pVal->vt = VT_BSTR;
pVal->bstrVal = T2BSTR( szValue );
return S_OK;
}
break;
}
return E_INVALIDARG;
}
STDMETHODIMP cDecalEnum::get_Group(BSTR *pVal)
{
if( pVal == NULL )
{
_ASSERT( FALSE );
return E_POINTER;
}
*pVal = OLE2BSTR( m_strGroup );
return S_OK;
}
STDMETHODIMP cDecalEnum::Skip (REFCLSID clsid)
{
// Search our loaded list for the clsid
for ( cKeyList::iterator i = m_keys.begin (); i != m_keys.end(); ++ i )
{
if ( clsid == i->m_clsid )
return openKeyAt ( i - m_keys.begin() );
}
return Error ( IDE_CLASSNOTFOUND );
}
STDMETHODIMP cDecalEnum::MoveBefore(REFCLSID clsidBefore)
{
if( m_key.m_hKey == NULL )
{
_ASSERT( FALSE );
return Error ( IDE_BADITERATOR );
}
// Find the entry we're inserting before
for ( cKeyList::iterator i = m_keys.begin (); i != m_keys.end(); ++ i )
{
if ( i->m_clsid == clsidBefore )
break;
}
if ( i == m_keys.end () )
return Error ( IDE_CLASSNOTFOUND );
int nFoundIndex = i - m_keys.begin ();
if ( nFoundIndex == m_nIndex )
// No op
return S_FALSE;
if ( nFoundIndex > m_nIndex )
-- nFoundIndex;
cKeyEntry eRemoved = m_keys[ m_nIndex ];
m_keys.erase ( m_keys.begin () + m_nIndex );
m_keys.insert ( m_keys.begin () + nFoundIndex, eRemoved );
// Reset so we're still on the same item
m_nIndex = nFoundIndex;
m_bOrderChanged = true;
return S_OK;
}