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

344 lines
No EOL
8.9 KiB
C++

// Image.cpp : Implementation of cImage
#include "stdafx.h"
#include "Inject.h"
#include "Image.h"
#include "Manager.h"
/////////////////////////////////////////////////////////////////////////////
// cImage
cImage::~cImage()
{
cManager::_p->removeImage( this );
}
void cImage::loadFile()
{
// ::MessageBox( NULL, _T( "cImage::loadFile" ), _T( "Inject.dll" ), MB_OK );
HANDLE hBitmap = ::CreateFile( m_szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL );
if( hBitmap == INVALID_HANDLE_VALUE )
{
int nError = ::GetLastError();
return;
}
BITMAPFILEHEADER bfh;
DWORD dwRead;
::ReadFile( hBitmap, &bfh, sizeof( BITMAPFILEHEADER ), &dwRead, NULL );
BITMAPINFOHEADER bih;
::ReadFile( hBitmap, &bih, sizeof( BITMAPINFOHEADER ), &dwRead, NULL );
RGBQUAD pal[ 256 ];
::memset( pal, 0, sizeof( RGBQUAD[ 256 ] ) );
// Figure out the number of colors used
int nEntries = 1 << ( bih.biBitCount );
if( bih.biClrUsed != 0 )
nEntries = bih.biClrUsed;
::ReadFile( hBitmap, pal, sizeof( RGBQUAD ) * nEntries, &dwRead, NULL );
SIZE szImage = { bih.biWidth, bih.biHeight };
cManager::_p->CreateCanvas( &szImage, &m_pImage );
CComPtr< IDirectDrawSurface4 > pSurface;
m_pImage->GetSurface( IID_IDirectDrawSurface4, reinterpret_cast< void ** >( &pSurface ) );
DDSURFACEDESC2 desc;
desc.dwSize = sizeof( DDSURFACEDESC2 );
pSurface->Lock( NULL, &desc, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR | DDLOCK_WRITEONLY, NULL );
WORD wDownMixed[ 256 ];
RGBQUAD *pSrc = pal;
for( WORD *pDest = wDownMixed; pDest != wDownMixed + ( nEntries - 1 ); ++ pDest, ++ pSrc ) {
m_pImage->DownMixRGB(pSrc->rgbRed, pSrc->rgbGreen, pSrc->rgbBlue, pDest);
}
*pDest = 0x07FF;
// Mekle: Added the (bih.biWidth % 4) sections below, as BMP rows MUST end on a 32bit boundry
BYTE *pbSurf = reinterpret_cast< BYTE * >( desc.lpSurface ) + ( desc.dwHeight - 1 ) * desc.lPitch;
BYTE *pbRow = new BYTE[ bih.biWidth + (bih.biWidth % 4) ];
for( int nRow = 0; nRow < bih.biHeight; ++ nRow )
{
::ReadFile( hBitmap, pbRow, bih.biWidth + (bih.biWidth % 4), &dwRead, NULL );
WORD *pwDest = reinterpret_cast< WORD * >( pbSurf );
for( BYTE *pbSrc = pbRow; pbSrc != pbRow + bih.biWidth; ++ pbSrc, ++ pwDest )
// Lookup the color in the downmixed palette
*pwDest = wDownMixed[ *pbSrc ];
pbSurf -= desc.lPitch;
}
delete[] pbRow;
pSurface->Unlock( NULL );
::CloseHandle( hBitmap );
}
void cImage::loadResource()
{
}
void cImage::loadPortal()
{
// ::MessageBox( NULL, _T( "cImage::loadPortal" ), _T( "Inject.dll" ), MB_OK );
struct cColorImage
{
DWORD m_dwID,
m_dwX,
m_dwY;
};
try
{
cDatFile::cFile icon = cManager::_p->m_portal.getFile( m_dwID );
cColorImage header;
icon.read( reinterpret_cast< BYTE * >( &header ), sizeof( cColorImage ) );
SIZE szImage = { header.m_dwX, header.m_dwY };
cManager::_p->CreateCanvas( &szImage, &m_pImage );
CComPtr< IDirectDrawSurface4 > pSurface;
m_pImage->GetSurface( IID_IDirectDrawSurface4, reinterpret_cast< void ** >( &pSurface ) );
DDSURFACEDESC2 desc;
desc.dwSize = sizeof( DDSURFACEDESC2 );
pSurface->Lock( NULL, &desc, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR | DDLOCK_WRITEONLY, NULL );
BYTE *pSrcLine = new BYTE[ 3 * header.m_dwX ],
*pEndLine = pSrcLine + 3 * header.m_dwX;
for( int i = 0; i < header.m_dwY; ++ i )
{
// Read in a line
icon.read( pSrcLine, 3 * header.m_dwX );
WORD *pwDest = reinterpret_cast< WORD * >( reinterpret_cast< BYTE * >( desc.lpSurface ) + desc.lPitch * i );
for( BYTE *pbSrc = pSrcLine; pbSrc != pEndLine; pbSrc += 3, ++ pwDest )
{
// Check for black (transparent)
if( pbSrc[ 0 ] == 0 && pbSrc[ 1 ] == 0 && pbSrc[ 2 ] == 0 )
*pwDest = 0x07FF;
else
{
// Downmix the pixel value
m_pImage->DownMixRGB(pbSrc[0], pbSrc[1], pbSrc[2], pwDest);
}
}
}
delete[] pSrcLine;
pSurface->Unlock( NULL );
}
catch( ... )
{
// Failure, file not found
}
}
bool cImage::testImage()
{
bool bReload = false;
if( m_pImage.p == NULL )
bReload = true;
else
{
VARIANT_BOOL bLost;
m_pImage->get_WasLost( &bLost );
if( bLost )
{
m_pImage.Release();
bReload = true;
}
}
if( bReload )
{
// Reload the image
switch( m_source )
{
case eFile:
loadFile();
break;
case eResource:
loadResource();
break;
case ePortal:
loadPortal();
break;
}
}
return ( m_pImage.p != NULL );
}
STDMETHODIMP cImage::PatBlt(ICanvas *pDest, LPRECT prcDest, LPPOINT ptOrigin)
{
// ::MessageBox( NULL, _T( "cImage::PatBlt" ), _T( "Inject.dll" ), MB_OK );
if( !testImage() )
return E_FAIL;
SIZE sz;
m_pImage->get_Size( &sz );
POINT ptAdjOrigin = { ptOrigin->x % sz.cx, ptOrigin->y % sz.cy };
if( ptAdjOrigin.x > 0 )
ptAdjOrigin.x -= sz.cx;
if( ptAdjOrigin.y > 0 )
ptAdjOrigin.y -= sz.cy;
RECT rcPlacement;
RECT rcSrc;
POINT ptSrcOrigin;
// Scan down
for( int y = prcDest->top + ptAdjOrigin.y; y < prcDest->bottom; y += sz.cy )
{
rcPlacement.top = y;
rcPlacement.bottom = y + sz.cy;
if( rcPlacement.top < prcDest->top )
rcPlacement.top = prcDest->top;
if( rcPlacement.bottom > prcDest->bottom )
rcPlacement.bottom = prcDest->bottom;
ptSrcOrigin.y = ( rcPlacement.top - prcDest->top - ptAdjOrigin.y ) % sz.cy;
rcSrc.top = ptSrcOrigin.y;
rcSrc.bottom = rcSrc.top + ( rcPlacement.bottom - rcPlacement.top );
// Scan across
for( int x = prcDest->left + ptAdjOrigin.x; x < prcDest->right; x += sz.cx )
{
rcPlacement.left = x;
rcPlacement.right = x + sz.cx;
if( rcPlacement.left < prcDest->left )
rcPlacement.left = prcDest->left;
if( rcPlacement.right > prcDest->right )
rcPlacement.right = prcDest->right;
ptSrcOrigin.x = ( rcPlacement.left - prcDest->left - ptAdjOrigin.x ) % sz.cx;
rcSrc.left = ptSrcOrigin.x;
rcSrc.right = rcSrc.left + ( rcPlacement.right - rcPlacement.left );
POINT pt = { rcPlacement.left, rcPlacement.top };
pDest->Blt( &rcSrc, m_pImage, &pt );
}
}
_ASSERTMEM( _CrtCheckMemory( ) );
return S_OK;
}
STDMETHODIMP cImage::StretchBlt(ICanvas *pDest, LPPOINT pptDest, long nWidth, long nStartStretch, long nEndStretch)
{
if( !testImage() )
return E_FAIL;
// Ok, the image is getting placed in three sections, fixed beginning and end
// and variable length middle sections (delimited by nStartStretch and nEndStretch)
// First we place the fixed beginning and end lengths
SIZE sz;
m_pImage->get_Size( &sz );
RECT rcSrcHead = { 0, 0, nStartStretch, sz.cy },
rcSrcTail = { nEndStretch, 0, sz.cx, sz.cy },
rcSrcMiddle = { nStartStretch, 0, nEndStretch, sz.cy };
int nTailPlacement = pptDest->x + nWidth - ( sz.cx - nEndStretch );
POINT ptHead = { pptDest->x, pptDest->y },
ptTail = { nTailPlacement, pptDest->y },
ptStretch = { 0, pptDest->y };
// Do the first Blts
pDest->Blt( &rcSrcHead, m_pImage, &ptHead );
pDest->Blt( &rcSrcTail, m_pImage, &ptTail );
// Now do the middle sections
for( ptStretch.x = pptDest->x + nStartStretch; ptStretch.x < nTailPlacement; ptStretch.x += ( nEndStretch - nStartStretch ) )
{
if( ( nTailPlacement - ptStretch.x ) < ( rcSrcMiddle.right - rcSrcMiddle.left ) )
rcSrcMiddle.right = rcSrcMiddle.left + ( nTailPlacement - ptStretch.x );
pDest->Blt( &rcSrcMiddle, m_pImage, &ptStretch );
}
return S_OK;
}
STDMETHODIMP cImage::Blt(LPRECT rcSrc, ICanvas *pDest, LPPOINT pptDest)
{
if( !testImage() )
return E_FAIL;
SIZE sz;
m_pImage->get_Size( &sz );
RECT rcImage = { 0, 0, sz.cx, sz.cy };
pDest->Blt( &rcImage, m_pImage, pptDest );
return S_OK;
}
STDMETHODIMP cImage::get_Size(SIZE *pVal)
{
if( !testImage() )
return E_FAIL;
m_pImage->get_Size( pVal );
return S_OK;
}
STDMETHODIMP cImage::StretchBltArea(LPRECT prcSrc, ICanvas *pDest, LPRECT prcDest)
{
if( !testImage() )
return E_FAIL;
SIZE sizeImage;
get_Size( &sizeImage );
if( prcSrc->right > sizeImage.cx )
prcSrc->right = sizeImage.cx ;
if( prcSrc->bottom > sizeImage.cy )
prcSrc->bottom = sizeImage.cy;
if( prcSrc->left < 0 )
prcSrc->left = 0;
if( prcSrc->top < 0 )
prcSrc->top = 0;
if( prcSrc->left >= prcSrc->right )
prcSrc->left = prcSrc->right - 1;
if( prcSrc->top >= prcSrc->bottom )
prcSrc->top = prcSrc->bottom - 1;
pDest->StretchBlt( prcSrc, m_pImage, prcDest );
return S_OK;
}