Код
#include <windows.h>
#include <stdio.h>
#include <setjmp.h>
#include <png.h>
#include <map>
//#include <Era.h>
using std::map;
//using namespace Era;
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
#pragma pack(1)
#define stdcall __stdcall
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
template<typename T1, typename T2>
inline T1 max( T1 Value1, T2 Value2 )
{
return ( Value1 > Value2 )? Value1 : Value2;
}
template<typename T1, typename T2>
inline T1 min( T1 Value1, T2 Value2 )
{
return ( Value1 < Value2 )? Value1 : Value2;
}
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
inline char tolower( char Char )
{
return ( Char >= 'A' && Char <= 'Z' )? Char - 'A' + 'a' : Char;
}
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
typedef unsigned char byte;
typedef unsigned int uint;
typedef long long int64;
typedef unsigned long long uint64;
typedef WORD color16;
typedef DWORD colorRGBA;
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
struct vector
{
int x, y;
vector( int x0 = 0, int y0 = 0 )
: x( x0 ), y( y0 )
{}
vector& operator+=( vector Other )
{
x += Other.x;
y += Other.y;
return *this;
}
vector& operator-=( vector Other )
{
x -= Other.x;
y -= Other.y;
return *this;
}
vector& operator/=( int Number )
{
x /= Number;
y /= Number;
return *this;
}
vector operator+( vector Other )
{
vector Result = *this;
Result += Other;
return Result;
}
vector operator-( vector Other )
{
vector Result = *this;
Result -= Other;
return Result;
}
vector operator/( int Number )
{
vector Result = *this;
Result /= Number;
return Result;
}
};
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
inline void rect_cross( vector *pDestPos, vector *pDestSize, vector OtherPos, vector OtherSize )
{
vector DestBottomRight = *pDestPos + *pDestSize;
vector OtherBottomRight = OtherPos + OtherSize;
pDestPos->x = max( pDestPos->x, OtherPos.x );
pDestPos->y = max( pDestPos->y, OtherPos.y );
DestBottomRight.x = min( DestBottomRight.x, OtherBottomRight.x );
DestBottomRight.y = min( DestBottomRight.y, OtherBottomRight.y );
*pDestSize = DestBottomRight - *pDestPos;
}
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
struct image_name
{
union
{
char chars[12];
DWORD dwords[3];
};
image_name( char *name )
{
for( int i = 0; i < 12; i++ )
chars[i] = tolower( name[i] );
}
bool operator<( image_name Other ) const
{
if( dwords[0] < Other.dwords[0] )
return true;
else if( dwords[0] > Other.dwords[0] )
return false;
else
{
if( dwords[1] < Other.dwords[1] )
return true;
else if( dwords[1] > Other.dwords[1] )
return false;
else
return dwords[2] < Other.dwords[2];
}
}
};
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
struct new_image
{
vector size;
colorRGBA data[0];
};
//-------------------------------------------------------------------------------------------------------
// Heroes III internal structure
//-------------------------------------------------------------------------------------------------------
struct image
{
/* 0*/ void *p1; // for many or all objects this is 0x63D6BC
/* 4*/ char name[12];
/*16*/ void *p2;
/*20*/ int unknown1;
/*24*/ int unknown2;
/*28*/ int unknown3;
/*32*/ int unknown4;
/*36*/ int type; // image type?
/*40*/ vector total_size;
/*48*/ vector size;
/*56*/ vector pos;
/*64*/ int d5; // = m_Size.x
/*68*/ int *pdata; // image data(pixels), format unknown
// may be more data here
};
//-------------------------------------------------------------------------------------------------------
// arguments for drawing functions
//-------------------------------------------------------------------------------------------------------
struct draw_info0
{
vector pos0;
vector size;
color16 *pscreen; // DDSURFACEDESC::pSurface
vector pos;
vector screen_size; // DDSURFACEDESC::dwWidth DDSURFACEDESC::dwHeight
int pitch; // DDSURFACEDESC::lPitch
int *a11;
union
{
int a12;
byte horz_invert;
color16 unknown_color;
};
union
{
int a13;
byte vert_invert; // not for everything // not supported
color16 color; // player color, obvodka
};
};
struct draw_info : draw_info0
{
int a14;
};
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
map<image_name, new_image*> g_Replace;
bool g_bPrint = false;
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
inline color16 MakeColor16( int R, int G, int B )
{
return (R << 11) | (G << 5) | (B << 0);
}
inline int GetRed ( color16 Color ) { return (Color>>11); }
inline int GetGreen ( color16 Color ) { return (Color>> 5)&0x3F; }
inline int GetBlue ( color16 Color ) { return (Color )&0x1F; }
inline int GetRed ( colorRGBA Color ) { return (Color )&0xFF; }
inline int GetGreen ( colorRGBA Color ) { return (Color>> 8)&0xFF; }
inline int GetBlue ( colorRGBA Color ) { return (Color>>16)&0xFF; }
inline int GetAlpha ( colorRGBA Color ) { return (Color>>24); }
//-------------------------------------------------------------------------------------------------------
// printf doesn't work
//-------------------------------------------------------------------------------------------------------
enum acolor
{
RED = FOREGROUND_RED | FOREGROUND_INTENSITY,
GREEN = FOREGROUND_GREEN | FOREGROUND_INTENSITY,
BLUE = FOREGROUND_BLUE | FOREGROUND_INTENSITY,
WHITE = RED | GREEN | BLUE,
};
static void aprintf( acolor Color, const char *pFormat, ... )
{
va_list Args;
va_start( Args, pFormat );
char Buffer[128];
vsprintf( Buffer, pFormat, Args );
va_end( Args );
DWORD Written;
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO BufferInfo;
GetConsoleScreenBufferInfo( hOutput, &BufferInfo );
SetConsoleTextAttribute( hOutput, (WORD)WHITE );
WriteConsole( hOutput, "alpha.dll: ", 11, &Written, NULL );
SetConsoleTextAttribute( hOutput, (WORD)Color );
WriteConsole( hOutput, Buffer, strlen(Buffer), &Written, NULL );
SetConsoleTextAttribute( hOutput, BufferInfo.wAttributes );
}
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
stdcall int image__sub_47BE90( image *pImage, draw_info0 DrawInfo ) asm("image__sub_47BE90");
stdcall int image__sub_47C9C0( image *pImage, draw_info0 DrawInfo ) asm("image__sub_47C9C0");
stdcall int image__sub_47D660( image *pImage, draw_info0 DrawInfo ) asm("image__sub_47D660");
stdcall int image__sub_47C300( image *pImage, draw_info DrawInfo ) asm("image__sub_47C300");
asm
(
".intel_syntax noprefix\n" // use intel syntax
"image__sub_47BE90: \n"
" pop eax \n"
" pop ecx \n"
" push eax \n"
" push ebp \n"
" mov ebp, esp \n"
" push ecx \n"
" mov eax, [ecx+0x24] \n"
" mov eax, 0x47BE97 \n"
" jmp eax \n"
"image__sub_47C9C0: \n"
" pop eax \n"
" pop ecx \n"
" push eax \n"
" push ebp \n"
" mov ebp, esp \n"
" push ecx \n"
" mov edx, ecx \n"
" mov eax, 0x47C9C6 \n"
" jmp eax \n"
"image__sub_47C300: \n"
//" pushad \n"
//" push 29502 \n"
//" mov eax, 0x74CE30 \n"
//" call eax \n"
//" add esp, 4 \n"
//" popad \n"
" pop eax \n"
" pop ecx \n"
" push eax \n"
" push ebp \n"
" mov ebp, esp \n"
" push ecx \n"
" mov al, [ebp+0x3C] \n"
" mov eax, 0x47C307 \n"
" jmp eax \n"
"image__sub_47D660: \n"
" pop eax \n"
" pop ecx \n"
" push eax \n"
" push ebp \n"
" mov ebp, esp \n"
" push ecx \n"
" mov edx, ecx \n"
" mov eax, 0x47D666 \n"
" jmp eax \n"
".att_syntax noprefix\n" // reset AT&T syntax or there will be errors
);
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
static int OriginalDraw( int Type, image *pImage, draw_info *pDrawInfo )
{
typedef stdcall int (*pfnOriginalDraw)( image *pImage, draw_info0 DrawInfo );
static pfnOriginalDraw pOriginalDraw[3] = { &image__sub_47BE90, &image__sub_47C9C0, &image__sub_47D660 };
if( Type == 3 )
return image__sub_47C300( pImage, *pDrawInfo );
else
return pOriginalDraw[Type]( pImage, (draw_info0)*pDrawInfo );
}
//-------------------------------------------------------------------------------------------------------
// Draw0 47BE90 - interface
// Draw1 47C9C0 - adventute map objects
// Draw2 47D660 - terrain
// Draw3 47C300 - monsters
//-------------------------------------------------------------------------------------------------------
static int Draw( int Type, image *pImage, draw_info *pInfo )
{
int screenW=GetSystemMetrics(SM_CXSCREEN);//Получить ширину экрана
int screenH=GetSystemMetrics(SM_CYSCREEN);//Получить высоту экрана
int NewRed, NewGreen, NewBlue;
color16 ro1,go1,bo1,rn1,gn1,bn1,an1;
if( pInfo->screen_size.x != screenW || pInfo->screen_size.y != screenH )
return OriginalDraw( Type, pImage, pInfo );
//asm
//(
//".intel_syntax noprefix\n" // use intel syntax
//" pushad \n"
//" push 29502 \n"
//" mov eax, 0x74CE30 \n"
//" call eax \n"
//" add esp, 4 \n"
//" popad \n"
//".att_syntax noprefix\n" // reset AT&T syntax or there will be errors
//);
static char LastNames[8][13] = { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0} };
static int CurLastName = 0;
//ExecErmCmd("VRv2007:Sd1;");
if( g_bPrint )
{
for( int i = 0; i < 8; i++ )
if( strcmp( pImage->name, LastNames[i] ) == 0 )
goto draw;
strncpy( LastNames[CurLastName], pImage->name, 12 );
LastNames[CurLastName][12] = 0;
aprintf( WHITE, "draw %s\n", LastNames[CurLastName] );
CurLastName++;
if( CurLastName >= 8 )
CurLastName = 0;
}
draw:
map<image_name,new_image*>::iterator Rep = g_Replace.find( image_name(pImage->name) );
if( Rep == g_Replace.end() )
return OriginalDraw( Type, pImage, pInfo );
new_image *pNewImage = Rep->second;
// center image
vector ImagePos = pInfo->pos - pInfo->pos0 + (pImage->total_size - pNewImage->size)/2;
// crop image
vector Pos = pInfo->pos;
vector Size = pInfo->size;
rect_cross( &Pos, &Size, ImagePos, pNewImage->size );
rect_cross( &Pos, &Size, vector( 0, 0 ), vector( screenW, screenH ) );
vector Pos0 = Pos - ImagePos;
color16 *pOld = &pInfo->pscreen[Pos.y*screenW + Pos.x];
colorRGBA *pNew;
if( pInfo->horz_invert )
pNew = &pNewImage->data[(Pos0.y+1)*pNewImage->size.x - Pos0.x];
else
pNew = &pNewImage->data[(Pos0.y )*pNewImage->size.x + Pos0.x];
for( int y = 0; y < Size.y; y++ )
{
for( int x = 0; x < Size.x; x++ )
{
ro1 = GetRed (*pOld);
rn1 = GetRed (*pNew);
go1 = GetGreen (*pOld);
gn1 = GetGreen (*pNew);
bo1 = GetBlue (*pOld);
bn1 = GetBlue (*pNew);
an1 = GetAlpha(*pNew);
if( ( *pNew & 0xFFFFFF ) == 0xFFFF ) // color for replacement
{
color16 New = pInfo->color;
if( New )
{
NewRed = ro1 + an1*(GetRed (New) - ro1)/256;
NewGreen = go1 + an1*(GetRed (New) - go1)/256;
NewBlue = bo1 + an1*(GetRed (New) - bo1)/256;
*pOld = MakeColor16( NewRed, NewGreen, NewBlue);
}
}
else
{
NewRed = ro1 + an1*(rn1 - ro1)/256;
NewGreen = go1 + an1*(gn1 - go1)/256;
NewBlue = bo1 + an1*(bn1 - bo1)/256;
*pOld = MakeColor16( NewRed, NewGreen, NewBlue);
}
pOld++;
if( pInfo->horz_invert )
pNew--;
else
pNew++;
}
pOld += screenW - Size.x;
if( pInfo->horz_invert )
pNew += pNewImage->size.x + Size.x;
else
pNew += pNewImage->size.x - Size.x;
}
return 0;
}
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
const char *g_pCurPngFile;
jmp_buf g_JumpBuffer;
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
void PngError( png_struct *png_ptr, const char *error_msg )
{
aprintf( RED, "libpng error while reading %s: %s\n", g_pCurPngFile, error_msg );
longjmp( g_JumpBuffer, 1 );
}
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
void PngWarning( png_struct *png_ptr, const char *warning_msg )
{
aprintf( BLUE, "libpng warning while reading %s: %s\n", g_pCurPngFile, warning_msg );
}
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
static void ReplaceImage( char *pName )
{
char FilePath[32];
sprintf( FilePath, "replace/%s", pName );
g_pCurPngFile = FilePath;
FILE *pNewImage = fopen( FilePath, "rb" );
if( !pNewImage )
{
aprintf( RED, "couldn't open file %s\n", FilePath );
return;
}
png_struct *png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, &PngError, &PngWarning );
png_info *info_ptr = png_create_info_struct(png_ptr);
png_init_io( png_ptr, pNewImage );
png_uint_32 width, height;
int color_type, bit_depth;
png_read_info( png_ptr, info_ptr );
png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL );
if( color_type == PNG_COLOR_TYPE_PALETTE )
png_set_palette_to_rgb(png_ptr);
if( color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
png_set_gray_to_rgb(png_ptr);
if( png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS ) )
png_set_tRNS_to_alpha(png_ptr);
if( bit_depth == 16 )
png_set_strip_16(png_ptr);
png_read_update_info( png_ptr, info_ptr );
new_image *pImage = (new_image*)malloc( sizeof(new_image) + width*height*4 );
pImage->size.x = width;
pImage->size.y = height;
colorRGBA **row_pointers = (colorRGBA**)malloc( height*sizeof(void*) );
for( png_uint_32 i = 0; i < height; i++ )
row_pointers[i] = pImage->data + width*i;
png_read_image( png_ptr, (byte**)row_pointers );
free( row_pointers );
png_read_end( png_ptr, NULL );
png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
fclose( pNewImage );
// prepare for conversion to 16 bit
byte *pCur = (byte*)pImage->data;
for( uint i = 0; i < width*height; i++ )
{
if( (pCur[0] == 255) && (pCur[1] == 255) && (pCur[2] == 0) ) // color for replacement
{
pCur += 4;
continue;
}
pCur[0] /= 8; // red
pCur[1] /= 4; // green
pCur[2] /= 8; // blue
pCur += 4;
}
pName[strlen(pName)-4] = 0;
g_Replace[image_name(pName)] = pImage;
}
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
static void LoadPNGs()
{
WIN32_FIND_DATA FindData;
HANDLE hSearch = FindFirstFile( "replace/*.png", &FindData );
if( hSearch == INVALID_HANDLE_VALUE )
{
aprintf( RED, "couldn't search \"replace/*.png\"\n" );
return;
}
do
{
if( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
continue;
int len = strlen(FindData.cFileName);
if( len > 12 + 4 || strcmp( &FindData.cFileName[len-4], ".png" ) != 0 )
continue;
if( setjmp( g_JumpBuffer ) == 0 )
ReplaceImage( FindData.cFileName );
}
while( FindNextFile( hSearch, &FindData ) );
FindClose( hSearch );
}
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
template<typename T> void* to_pvoid( T Value )
{
return *(void**)&Value;
}
//-------------------------------------------------------------------------------------------------------
// write "jmp pNew" at address pOriginal
//-------------------------------------------------------------------------------------------------------
inline void WriteHook( void *pOriginal, void *pNew, uint Test )
{
if( !IsBadReadPtr( pOriginal, 8 ) )
{
if( ((uint*)pOriginal)[0] != 0x51EC8B55 || ((uint*)pOriginal)[1] != Test )
{
char Msg[256];
sprintf( Msg, "Memory at 0x%X is not beginning of expected function. "
"Possibly you try to run wrong version of Heroes III with alpha.dll or inject alpha.dll multiple times.\n"
"Do you want to insert hook function?", (uint)pOriginal );
if( MessageBox( NULL, Msg, "alpha.dll", MB_ICONERROR | MB_YESNO ) != IDYES )
return;
}
}
if( IsBadWritePtr( pOriginal, 5 ) )
{
char Msg[100];
sprintf( Msg, "No write access to 0x%X. Disable write protection for section \".text\" and try again.",
(uint)pOriginal );
MessageBox( NULL, Msg, "alpha.dll", MB_ICONERROR );
return;
}
*(byte*)pOriginal = 0xE9; // jmp
*(void**)( (byte*)pOriginal + 1 ) = (void*)( (byte*)pNew - (byte*)pOriginal - 5 );
aprintf( GREEN, "write 0x%X: jmp 0x%X\n", pOriginal, pNew );
}
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
stdcall int image__hook_47BE90( draw_info0 Info )
{
register void *ecx asm("ecx"); // get access to ecx where this pointer is stored
return Draw( 0, (image*)ecx, (draw_info*)&Info );
}
stdcall int image__hook_47C9C0( draw_info0 Info )
{
register void *ecx asm("ecx"); // get access to ecx where this pointer is stored
return Draw( 1, (image*)ecx, (draw_info*)&Info );
}
stdcall int image__hook_47D660( draw_info0 Info )
{
register void *ecx asm("ecx"); // get access to ecx where this pointer is stored
return Draw( 2, (image*)ecx, (draw_info*)&Info );
}
stdcall int image__hook_47C300( draw_info Info )
{
register void *ecx asm("ecx"); // get access to ecx where this pointer is stored
return Draw( 3, (image*)ecx, (draw_info*)&Info );
}
//-------------------------------------------------------------------------------------------------------
//
//-------------------------------------------------------------------------------------------------------
extern "C" stdcall BOOL DllMain( HINSTANCE hInstanceDLL, DWORD Reason, void *pReserved )
{
if( Reason != DLL_PROCESS_ATTACH )
return TRUE;
int argc;
wchar_t **argv = CommandLineToArgvW( GetCommandLineW(), &argc );
while( argc-- )
{
if( wcscmp( argv[argc], L"-alpha-console" ) == 0 )
AllocConsole();
if( wcscmp( argv[argc], L"-alpha-print" ) == 0 )
g_bPrint = true;
}
LocalFree( argv );
aprintf( GREEN, "alpha.dll loaded at address 0x%X\n", hInstanceDLL );
WriteHook( (void*)0x0047BE90, to_pvoid(&image__hook_47BE90), 0x5324418B );
WriteHook( (void*)0x0047C9C0, to_pvoid(&image__hook_47C9C0), 0x5653D18B );
WriteHook( (void*)0x0047D660, to_pvoid(&image__hook_47D660), 0x5653D18B );
WriteHook( (void*)0x0047C300, to_pvoid(&image__hook_47C300), 0x533C458A );
LoadPNGs();
return TRUE;
}