openDecal/Native/Inject/IconCache.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

291 lines
8.7 KiB
C++

// IconCache.cpp : Implementation of cIconCache
#include "stdafx.h"
#include "Inject.h"
#include "IconCache.h"
#include "Manager.h"
//Moputu - 05172002: Added long lColor to make icons with different colored borders unique.
bool cIconCache::findIcon( HMODULE hMod, DWORD dwFile, cIconCache::cIconBuffer *&pBuffer, int &nIndex, long lColor )
{
for( cIconBufferList::iterator i = m_icons.begin(); i != m_icons.end(); )
{
VARIANT_BOOL bLost;
i->m_pSurface->get_WasLost( &bLost );
if( bLost )
{
i = m_icons.erase( i );
continue;
}
// Search inside this buffer
for( cIDList::iterator j = i->m_icons.begin(); j != i->m_icons.end(); ++ j )
{
//if( j->m_hMod == hMod && j->m_dwID == dwFile ) // Original Code
if( j->m_hMod == hMod && j->m_dwID == dwFile && j->m_lBColor == lColor )
{
// We found the icon, return happy
pBuffer = &( *i );
nIndex = j - i->m_icons.begin();
return true;
}
}
++ i;
}
// Icon not found
return false;
}
void cIconCache::findFreeSlot( cIconBuffer *&pBuffer, int &nIndex )
{
// First find a buffer with an unused icon slot, note that we test
// the buffers
for( cIconBufferList::iterator i = m_icons.begin(); i != m_icons.end(); )
{
VARIANT_BOOL bLost;
i->m_pSurface->get_WasLost( &bLost );
if( bLost )
{
i = m_icons.erase( i );
continue;
}
if( i->m_icons.size() < ( m_nEdge * m_nEdge ) )
break;
++ i;
}
if( i == m_icons.end() )
{
// There were no surfaces with free slots, make a new one
cIconBuffer buf;
buf.m_icons.reserve( m_nEdge * m_nEdge );
SIZE sz = { m_szIcon.cx * m_nEdge, m_szIcon.cy * m_nEdge };
cManager::_p->CreateCanvas( &sz, &buf.m_pSurface );
m_icons.push_back( buf );
// Reset to last entry
i = -- m_icons.end();
}
pBuffer = &( *i );
nIndex = i->m_icons.size();
}
//Modified by Moputu 05/12/2002 to allow the replacement of icon border colors.
bool cIconCache::loadIcon( DWORD dwFile, cIconCache::cIconBuffer *&pBuffer, int &nIndex, long lColor )
{
findFreeSlot( pBuffer, nIndex );
struct cColorImage
{
DWORD m_dwID,
m_dwX,
m_dwY;
};
try
{
cDatFile::cFile icon = cManager::_p->m_portal.getFile( dwFile );
cColorImage header;
icon.read( reinterpret_cast< BYTE * >( &header ), sizeof( cColorImage ) );
int nXScale, nYScale;
if( header.m_dwX % m_szIcon.cx != 0 )
{
// Bad size - only linear filtering here
_ASSERT( FALSE );
return false;
}
nXScale = header.m_dwX / m_szIcon.cx;
if( header.m_dwY % m_szIcon.cy != 0 )
{
// Bad size - only linear filtering here
_ASSERT( FALSE );
return false;
}
nYScale = header.m_dwY / m_szIcon.cy;
// Can't fail now, transfering bits to the buffer
POINT ptIconOrg = { ( nIndex % m_nEdge ) * m_szIcon.cx, ( nIndex / m_nEdge ) * m_szIcon.cy };
RECT rcSrc = { ptIconOrg.x, ptIconOrg.y, ptIconOrg.x + m_szIcon.cx, ptIconOrg.y + m_szIcon.cy };
DDSURFACEDESC2 desc;
desc.dwSize = sizeof( DDSURFACEDESC2 );
CComPtr< IDirectDrawSurface4 > pSurface;
pBuffer->m_pSurface->GetSurface( IID_IDirectDrawSurface4, reinterpret_cast< void ** >( &pSurface ) );
pSurface->Lock( &rcSrc, &desc, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR | DDLOCK_WRITEONLY, NULL );
BYTE srcLine[ 3 * 64 ],
*pEndLine = srcLine + 3 * header.m_dwX;
{
for( int i = 0; i < m_szIcon.cy; ++ i )
{
// Read in a line
icon.read( srcLine, 3 * header.m_dwX );
icon.skip( 3 * header.m_dwX * ( nYScale - 1 ) );
WORD *pwDest = reinterpret_cast< WORD * >( reinterpret_cast< BYTE * >( desc.lpSurface ) + desc.lPitch * i );
for( BYTE *pbSrc = srcLine; pbSrc != pEndLine; pbSrc += 3 * nXScale, ++ pwDest )
{
// Check for black (transparent)
if( ( pbSrc[ 0 ] == 0 && pbSrc[ 1 ] == 0 && pbSrc[ 2 ] == 0 ) ||
( pbSrc[ 0 ] == 255 && pbSrc[ 1 ] == 255 && pbSrc[ 2 ] == 255 ) )
// *pwDest = 0x07FF; // Original code
if( pbSrc[ 0 ] == 255 && pbSrc[ 1 ] == 255 && pbSrc[ 2 ] == 255 ) //Check for White (border)
{
if ( lColor == -1 ) // No argument was passed, treat it like the original code would have.
{
*pwDest = 0x07FF;
}
else //Replace with specified color.
{
pBuffer->m_pSurface->DownMixRGB((BYTE)lColor, (BYTE)(lColor >> 8), (BYTE)(lColor >> 16), pwDest);
}
}
else
{
*pwDest = 0x07FF;
}
else
{
pBuffer->m_pSurface->DownMixRGB(pbSrc[0], pbSrc[1], pbSrc[2], pwDest);
}
}
}
}
pSurface->Unlock( &rcSrc );
cIconID iid = { NULL, dwFile, lColor }; //Moputu - 05172002: Added lColor to make icons with different colored borders unique.
pBuffer->m_icons.push_back( iid );
// Survived the encounter
return true;
}
catch( ... )
{
// Failure, return false at end
}
// File not found, fail
_ASSERT( FALSE );
return false;
}
bool cIconCache::loadIconResource( HMODULE hMod, DWORD dwResourceID, cIconBuffer *&pBuffer, int &nIndex, long lColor )
{
HICON hIcon = reinterpret_cast< HICON >( ::LoadImage( hMod, MAKEINTRESOURCE( dwResourceID ), IMAGE_ICON, m_szIcon.cx, m_szIcon.cy, LR_DEFAULTCOLOR ) );
if( hIcon == NULL )
return false;
findFreeSlot( pBuffer, nIndex );
// TODO: Write a fast version of this function, use GDI for now
HDC hdc;
pBuffer->m_pSurface->GetDC( &hdc );
HBRUSH br = ::CreateSolidBrush( RGB( 0, 255, 255 ) );
POINT ptIconOrg = { ( nIndex % m_nEdge ) * m_szIcon.cx, ( nIndex / m_nEdge ) * m_szIcon.cy };
RECT rcSrc = { ptIconOrg.x, ptIconOrg.y, ptIconOrg.x + m_szIcon.cx, ptIconOrg.y + m_szIcon.cy };
::FillRect( hdc, &rcSrc, br );
::DrawIconEx( hdc, ptIconOrg.x, ptIconOrg.y, hIcon, m_szIcon.cx, m_szIcon.cy, 0, NULL, DI_NORMAL );
::DeleteObject( br );
pBuffer->m_pSurface->ReleaseDC();
::DestroyIcon( hIcon );
cIconID iid = { hMod, dwResourceID, lColor }; //Moputu - 05172002: Added long lColor to make icons with different colored borders unique.
pBuffer->m_icons.push_back( iid );
return true;
}
void cIconCache::drawIcon( LPPOINT pPos, ICanvas *pDest, cIconBuffer *pBuffer, int nIndex )
{
_ASSERTE( pPos != NULL );
_ASSERTE( pDest != NULL );
_ASSERTE( pBuffer != NULL );
_ASSERTE( nIndex >= 0 && nIndex < ( m_nEdge * m_nEdge ) );
POINT ptIconOrg = { ( nIndex % m_nEdge ) * m_szIcon.cx, ( nIndex / m_nEdge ) * m_szIcon.cy };
RECT rcSrc = { ptIconOrg.x, ptIconOrg.y, ptIconOrg.x + m_szIcon.cx, ptIconOrg.y + m_szIcon.cy };
pDest->Blt( &rcSrc, pBuffer->m_pSurface, pPos );
}
/////////////////////////////////////////////////////////////////////////////
// cIconCache
STDMETHODIMP cIconCache::DrawIcon( LPPOINT ppt, long nFile, long nModule, ICanvas *pCanvas )
{
_ASSERTE( ppt != NULL );
_ASSERTE( pCanvas != NULL );
cIconBuffer *pBuffer;
int nIndex;
// GKusnick: Handle no-icon case without asserting.
if (nModule == 0 && nFile == 0) return S_OK;
if( !findIcon( reinterpret_cast< HMODULE >( nModule ), *reinterpret_cast< DWORD* >( &nFile ), pBuffer, nIndex ) )
{
if( nModule == 0 )
{
if( !loadIcon( *reinterpret_cast< DWORD* >( &nFile ), pBuffer, nIndex ) )
return E_FAIL;
}
else
{
if( !loadIconResource( reinterpret_cast< HMODULE >( nModule ), *reinterpret_cast< DWORD* >( &nFile ), pBuffer, nIndex ) )
return E_FAIL;
}
}
drawIcon( ppt, pCanvas, pBuffer, nIndex );
return S_OK;
}
STDMETHODIMP cIconCache::DrawIconEx( LPPOINT ppt, long nFile, long nModule, ICanvas *pCanvas, long lColor )
{
_ASSERTE( ppt != NULL );
_ASSERTE( pCanvas != NULL );
cIconBuffer *pBuffer;
int nIndex;
if( !findIcon( reinterpret_cast< HMODULE >( nModule ), *reinterpret_cast< DWORD* >( &nFile ), pBuffer, nIndex, lColor ) ) //Moputu - 05172002: Added long lColor to make icons with different colored borders unique.
{
if( nModule == 0 )
{
if( !loadIcon( *reinterpret_cast< DWORD* >( &nFile ), pBuffer, nIndex, lColor ) )
return E_FAIL;
}
else
{
if( !loadIconResource( reinterpret_cast< HMODULE >( nModule ), *reinterpret_cast< DWORD* >( &nFile ), pBuffer, nIndex, lColor ) ) //Moputu - 05172002: Added long lColor to make icons with different colored borders unique.
return E_FAIL;
}
}
drawIcon( ppt, pCanvas, pBuffer, nIndex );
return S_OK;
}