|
|
sandruse

 
Tavern Dweller
|
posted November 03, 2025 03:51 PM |
|
Edited by sandruse at 16:10, 03 Nov 2025.
|
About HDMod dll plugin FOR HELP
Hello there! I try to create a small HDMod dll plugin. However, the game will crash when the logic run into the if area. Any Friend could help me? Thanks!
#include <stdint.h>
#include "patcher_x86.hpp"
Patcher* _P;
PatcherInstance* _PI;
static _bool_ plugin_On = 0;
int __stdcall SS(LoHook* h, HookContext* c)
{
int SSName = *(int*)(c->ebp + 0x08); // SS ID
unsigned char SSLevel = *(unsigned char*)(c->esi + c->ecx + 0x0C9); // Curent SS Level
if (SSName == 11 && SSLevel == 1)// SKILL_EAGLE_EYE
{
// ADD Primary Skill
*(uint8_t*)(c->ecx + 0x476) = 15;
}
__asm // The asm LoHook ocupy
{
add al, byte ptr [ebp+0Ch]
mov [edx], al
}
return EXEC_DEFAULT;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
if ( !plugin_On )
{
plugin_On = 1;
_P = GetPatcher();
_PI = _P->CreateInstance("HD.Plugin.Rules_1.7");
_PI->WriteLoHook(0x4E255D, SS);
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
|
|
AlexSpl

   
    
Responsible
Supreme Hero
|
posted November 04, 2025 03:02 AM |
|
|
This code seems to be correct with the exception of this:
__asm // The asm LoHook ocupy
{
add al, byte ptr [ebp+0Ch]
mov [edx], al
}
You don't need it as you already wrote EXEC_DEFAULT, which means the patcher will execute those instructions after it's done with your hook's code. I don't know exactly, but supposedly registers in __asm sections are treated as they are (I mean, for example, that c->edx may actually be stored in another register within a hook and even in the memory, while edx in the __asm section is the actual edx register which may store anything at the moment).
|
|
sandruse

 
Tavern Dweller
|
posted November 04, 2025 11:46 AM |
|
|
AlexSpl said: This code seems to be correct with the exception of this:
__asm // The asm LoHook ocupy
{
add al, byte ptr [ebp+0Ch]
mov [edx], al
}
You don't need it as you already wrote EXEC_DEFAULT, which means the patcher will execute those instructions after it's done with your hook's code. I don't know exactly, but supposedly registers in __asm sections are treated as they are (I mean, for example, that c->edx may actually be stored in another register within a hook and even in the memory, while edx in the __asm section is the actual edx register which may store anything at the moment).
Thank you for your answer, AlesSpl! The place you mentioned is also what I suspect, but due to my limited computer programming skills, I don't know how to handle it either. However, I re-edited the previous code, rewrote the logic of the original function, and then embedded my own logic. It seems that there are no problems after testing.
This is a simple Mod to slightly balance the EagleEye:
Hero will get +1 Power when get EagleEye on second and third level. And the hero who have the special EagleEye will get and extra +1 on the third level.
|
|
sandruse

 
Tavern Dweller
|
posted November 04, 2025 11:48 AM |
|
|
#include "patcher_x86.hpp"
// HDMod dll hack: The Eagle Eye update will give extra Magic Power
Patcher* _P;
PatcherInstance* _PI;
static _bool_ plugin_On = 0;
int __stdcall MyGiveSS(LoHook* h, HookContext* c) {
// get 'hero' Pointer(ecx)
void* hero = (void*)c->ecx;
// get the 2 formal parameters of the hooked function
int iWhichSS = *(int*)(c->ebp + 8);
int iNumLevelsToGive = *(int*)(c->ebp + 0x0C);
// get the val and pointer of hero's second skill level
unsigned char* skillLevel = (unsigned char*)hero + 0xC9 + iWhichSS;
int currentLevel = *skillLevel;
if (currentLevel > 0) {
*skillLevel += iNumLevelsToGive;
} else {
int* skillCount = (int*)((char*)hero + 0x101);
if (*skillCount < 8) {
*skillLevel = iNumLevelsToGive;
*(unsigned char*)((char*)hero + 0xE5 + iWhichSS) = (unsigned char)(*skillCount + 1);
(*skillCount)++;
}
}
if (*skillLevel > 3) {
*skillLevel = 3;
}
int result = *skillLevel - currentLevel;
// the hack content
// if hero get level 2 eagle eye, power +1
if (currentLevel <= 1 && *skillLevel >= 2 && iWhichSS == 11) {
*(unsigned char*)((char*)hero + 0x476 + 2) += 1; // Hero's 3rd Primary Power: Magic Power
}
// if hero get level 3 eagle eye, power +1
if (result >= 1 && *skillLevel == 3 && iWhichSS == 11) {
// if hero's special is eagle eye then get an extra +1
switch (*(int*)((char*)hero + 0x1A)) { // HeroID
case 13: // Sanya
case 28: // Malcom
case 42: // Serena
case 75: // Nimbus
case 92: // Geon
case 110: // Oris
case 127: // Tiva
*(unsigned char*)((char*)hero + 0x476 + 2) += 1;
}
*(unsigned char*)((char*)hero + 0x476 + 2) += 1;
}
c->eax = result; // set the retrun val
c->return_address = 0x4E259B; // set the return position
return EXEC_DEFAULT;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
if ( !plugin_On )
{
plugin_On = 1;
_P = GetPatcher();
_PI = _P->CreateInstance("HD.Plugin.Test_2025-11-4");
_PI->WriteLoHook(0x004E2548, MyGiveSS);
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
|
|
AlexSpl

   
    
Responsible
Supreme Hero
|
posted November 04, 2025 07:05 PM |
|
|
Great The next step would be to get rid of numerical offsets. For this you may use some APIs (the best one I can recommend at the moment is NH3API). With API you will no longer need to remember offsets or search for them in a database. You will be able to use field and methods of a class in your code in a readable form, like -
hero->SSLevel[HSS_EAGLE_EYE]
for this -
*(unsigned char*)(c->esi + c->ecx + 0x0C9); // Curent SS Level
|
|
sandruse

 
Tavern Dweller
|
posted November 05, 2025 11:37 AM |
|
|
I've found that lib on github. And it helped me to get the correct hex offset. Since it only works on VC2017 or higher, I can't use it with my VC2010
Later may I have more spare time to make good use with it.
Thank you AlesSpl again! Your codes on handbookhmm inspire me a lot too.
|
| |
|
|