Added a new hook: HOOK_ADJUSTRADS

Fixed a crash bug in ScriptExtender.cpp from commit c736e39.
Some adjustments to the hook injection mechanism.
This commit is contained in:
NovaRain
2021-01-13 11:11:32 +08:00
parent 4535ec9e20
commit bde6b94855
10 changed files with 125 additions and 75 deletions
+4 -1
View File
@@ -806,7 +806,10 @@
#define bit_32 (0x80000000) #define bit_32 (0x80000000)
#define bit_ALL (0xFFFFFFFF) #define bit_ALL (0xFFFFFFFF)
#define ROLL_CRITICAL_FAILURE 0
#define ROLL_FAILURE 1
#define ROLL_SUCCESS 2
#define ROLL_CRITICAL_SUCCESS 3
//Misc commands //Misc commands
#define obj_get_rot(obj) (has_trait(TRAIT_OBJECT, obj, OBJECT_CUR_ROT)) #define obj_get_rot(obj) (has_trait(TRAIT_OBJECT, obj, OBJECT_CUR_ROT))
+1
View File
@@ -70,6 +70,7 @@
#define HOOK_TARGETOBJECT (42) #define HOOK_TARGETOBJECT (42)
#define HOOK_ENCOUNTER (43) #define HOOK_ENCOUNTER (43)
#define HOOK_ADJUSTPOISON (44) #define HOOK_ADJUSTPOISON (44)
#define HOOK_ADJUSTRADS (45)
//Valid arguments to list_begin //Valid arguments to list_begin
#define LIST_CRITTERS (0) #define LIST_CRITTERS (0)
+11
View File
@@ -707,3 +707,14 @@ int arg2 - the damage value at the time of applying the poison effect
int ret0 - the new amount of poison being added/removed int ret0 - the new amount of poison being added/removed
int ret1 - the new damage value, only negative values are allowed (will only be valid at the time of taking damage from the poison) int ret1 - the new damage value, only negative values are allowed (will only be valid at the time of taking damage from the poison)
-------------------------------------------
HOOK_ADJUSTRADS (hs_adjustrads.int)
Runs when a critter's radiation level is changed.
Critter arg0 - the critter (usually dude_obj)
int arg1 - the amount of radiation being added/removed
int ret0 - the new amount of radiation being added/removed
-2
View File
@@ -399,9 +399,7 @@ void DamageMod::init() {
case 1: case 1:
case 2: case 2:
case 5: case 5:
if (!HookScripts::IsInjectHook(HOOK_SUBCOMBATDAMAGE)) {
HookScripts::InjectingHook(HOOK_SUBCOMBATDAMAGE); HookScripts::InjectingHook(HOOK_SUBCOMBATDAMAGE);
}
break; break;
default: default:
formula = 0; formula = 0;
+59 -62
View File
@@ -40,16 +40,18 @@ namespace sfall
// Number of types of hooks // Number of types of hooks
static constexpr int numHooks = HOOK_COUNT; static constexpr int numHooks = HOOK_COUNT;
bool HookScripts::injectAllHooks; bool injectAllHooks;
DWORD HookScripts::initingHookScripts; DWORD HookScripts::initingHookScripts;
std::vector<HookFile> HookScripts::hookScriptFilesList; std::vector<HookFile> HookScripts::hookScriptFilesList;
typedef void(*HookInjectFunc)(); typedef void(*HookInjectFunc)();
struct HooksInjectInfo { struct HooksInjectInfo {
int id; int id;
HookInjectFunc inject; HookInjectFunc inject;
bool isInject; char injectState;
}; };
static struct HooksPositionInfo { static struct HooksPositionInfo {
@@ -59,62 +61,64 @@ static struct HooksPositionInfo {
} hooksInfo[numHooks]; } hooksInfo[numHooks];
static HooksInjectInfo injectHooks[] = { static HooksInjectInfo injectHooks[] = {
{HOOK_TOHIT, Inject_ToHitHook, false}, {HOOK_TOHIT, Inject_ToHitHook, 0},
{HOOK_AFTERHITROLL, Inject_AfterHitRollHook, false}, {HOOK_AFTERHITROLL, Inject_AfterHitRollHook, 0},
{HOOK_CALCAPCOST, Inject_CalcApCostHook, false}, {HOOK_CALCAPCOST, Inject_CalcApCostHook, 0},
{HOOK_DEATHANIM1, Inject_DeathAnim1Hook, false}, {HOOK_DEATHANIM1, Inject_DeathAnim1Hook, 0},
{HOOK_DEATHANIM2, Inject_DeathAnim2Hook, false}, {HOOK_DEATHANIM2, Inject_DeathAnim2Hook, 0},
{HOOK_COMBATDAMAGE, Inject_CombatDamageHook, false}, {HOOK_COMBATDAMAGE, Inject_CombatDamageHook, 0},
{HOOK_ONDEATH, Inject_OnDeathHook, false}, {HOOK_ONDEATH, Inject_OnDeathHook, 0},
{HOOK_FINDTARGET, Inject_FindTargetHook, false}, {HOOK_FINDTARGET, Inject_FindTargetHook, 0},
{HOOK_USEOBJON, Inject_UseObjOnHook, false}, {HOOK_USEOBJON, Inject_UseObjOnHook, 0},
{HOOK_REMOVEINVENOBJ, Inject_RemoveInvenObjHook, false}, {HOOK_REMOVEINVENOBJ, Inject_RemoveInvenObjHook, 0},
{HOOK_BARTERPRICE, Inject_BarterPriceHook, false}, {HOOK_BARTERPRICE, Inject_BarterPriceHook, 0},
{HOOK_MOVECOST, Inject_MoveCostHook, false}, {HOOK_MOVECOST, Inject_MoveCostHook, 0},
{HOOK_HEXMOVEBLOCKING, Inject_HexMoveBlockHook, false}, {HOOK_HEXMOVEBLOCKING, Inject_HexMoveBlockHook, 0},
{HOOK_HEXAIBLOCKING, Inject_HexIABlockHook, false}, {HOOK_HEXAIBLOCKING, Inject_HexIABlockHook, 0},
{HOOK_HEXSHOOTBLOCKING, Inject_HexShootBlockHook, false}, {HOOK_HEXSHOOTBLOCKING, Inject_HexShootBlockHook, 0},
{HOOK_HEXSIGHTBLOCKING, Inject_HexSightBlockHook, false}, {HOOK_HEXSIGHTBLOCKING, Inject_HexSightBlockHook, 0},
{HOOK_ITEMDAMAGE, Inject_ItemDamageHook, false}, {HOOK_ITEMDAMAGE, Inject_ItemDamageHook, 0},
{HOOK_AMMOCOST, Inject_AmmoCostHook, false}, {HOOK_AMMOCOST, Inject_AmmoCostHook, 0},
{HOOK_USEOBJ, Inject_UseObjHook, false}, {HOOK_USEOBJ, Inject_UseObjHook, 0},
{HOOK_KEYPRESS, nullptr, true}, // no embed code to the engine {HOOK_KEYPRESS, nullptr, 2}, // no embed code to the engine
{HOOK_MOUSECLICK, nullptr, true}, // no embed code to the engine {HOOK_MOUSECLICK, nullptr, 2}, // no embed code to the engine
{HOOK_USESKILL, Inject_UseSkillHook, false}, {HOOK_USESKILL, Inject_UseSkillHook, 0},
{HOOK_STEAL, Inject_StealCheckHook, false}, {HOOK_STEAL, Inject_StealCheckHook, 0},
{HOOK_WITHINPERCEPTION, Inject_WithinPerceptionHook, false}, {HOOK_WITHINPERCEPTION, Inject_WithinPerceptionHook, 0},
{HOOK_INVENTORYMOVE, Inject_InventoryMoveHook, false}, {HOOK_INVENTORYMOVE, Inject_InventoryMoveHook, 0},
{HOOK_INVENWIELD, Inject_InvenWieldHook, false}, {HOOK_INVENWIELD, Inject_InvenWieldHook, 0},
{HOOK_ADJUSTFID, nullptr, true}, // always embedded to the engine {HOOK_ADJUSTFID, nullptr, 2}, // always embedded to the engine
{HOOK_COMBATTURN, Inject_CombatTurnHook, false}, {HOOK_COMBATTURN, Inject_CombatTurnHook, 0},
{HOOK_CARTRAVEL, Inject_CarTravelHook, false}, {HOOK_CARTRAVEL, Inject_CarTravelHook, 0},
{HOOK_SETGLOBALVAR, Inject_SetGlobalVarHook, false}, {HOOK_SETGLOBALVAR, Inject_SetGlobalVarHook, 0},
{HOOK_RESTTIMER, Inject_RestTimerHook, false}, {HOOK_RESTTIMER, Inject_RestTimerHook, 0},
{HOOK_GAMEMODECHANGE, nullptr, true}, // always embedded to the engine {HOOK_GAMEMODECHANGE, nullptr, 2}, // always embedded to the engine
{HOOK_USEANIMOBJ, Inject_UseAnimateObjHook, false}, {HOOK_USEANIMOBJ, Inject_UseAnimateObjHook, 0},
{HOOK_EXPLOSIVETIMER, Inject_ExplosiveTimerHook, false}, {HOOK_EXPLOSIVETIMER, Inject_ExplosiveTimerHook, 0},
{HOOK_DESCRIPTIONOBJ, Inject_DescriptionObjHook, false}, {HOOK_DESCRIPTIONOBJ, Inject_DescriptionObjHook, 0},
{HOOK_USESKILLON, Inject_UseSkillOnHook, false}, {HOOK_USESKILLON, Inject_UseSkillOnHook, 0},
{HOOK_ONEXPLOSION, Inject_OnExplosionHook, false}, {HOOK_ONEXPLOSION, Inject_OnExplosionHook, 0},
{HOOK_SUBCOMBATDAMAGE, Inject_SubCombatDamageHook, false}, // replace the code logic {HOOK_SUBCOMBATDAMAGE, Inject_SubCombatDamageHook, 0}, // replace the code logic
{HOOK_SETLIGHTING, Inject_SetLightingHook, false}, {HOOK_SETLIGHTING, Inject_SetLightingHook, 0},
{HOOK_SNEAK, Inject_SneakCheckHook, false}, {HOOK_SNEAK, Inject_SneakCheckHook, 0},
{HOOK_STDPROCEDURE, Inject_ScriptProcedureHook, false}, {HOOK_STDPROCEDURE, Inject_ScriptProcedureHook, 0},
{HOOK_STDPROCEDURE_END, Inject_ScriptProcedureHook2, false}, {HOOK_STDPROCEDURE_END, Inject_ScriptProcedureHook2, 0},
{HOOK_TARGETOBJECT, Inject_TargetObjectHook, false}, {HOOK_TARGETOBJECT, Inject_TargetObjectHook, 0},
{HOOK_ENCOUNTER, Inject_EncounterHook, false}, {HOOK_ENCOUNTER, Inject_EncounterHook, 0},
{HOOK_ADJUSTPOISON, Inject_AdjustPoisonHook, false}, {HOOK_ADJUSTPOISON, Inject_AdjustPoisonHook, 0},
{HOOK_ADJUSTRADS, Inject_AdjustRadsHook, 1}, // always embedded for party control fix
}; };
void HookScripts::InjectingHook(int hookId) { void HookScripts::InjectingHook(int hookId) {
if (!injectHooks[hookId].isInject && injectHooks[hookId].id == hookId) { if (!IsInjectHook(hookId) && injectHooks[hookId].id == hookId) {
injectHooks[hookId].isInject = true; injectHooks[hookId].injectState = 2;
injectHooks[hookId].inject(); injectHooks[hookId].inject();
devlog_f("Inject hook ID: %d\n", DL_INIT, hookId);
} }
} }
bool HookScripts::IsInjectHook(int hookId) { bool HookScripts::IsInjectHook(int hookId) {
return injectHooks[hookId].isInject; return injectHooks[hookId].injectState == 2;
} }
bool HookScripts::HookHasScript(int hookId) { bool HookScripts::HookHasScript(int hookId) {
@@ -146,16 +150,8 @@ void HookScripts::RegisterHook(fo::Program* script, int id, int procNum, bool sp
} }
hooks[id].insert(c_it, hook); hooks[id].insert(c_it, hook);
switch (id) {
case HOOK_KEYPRESS:
case HOOK_MOUSECLICK:
case HOOK_ADJUSTFID:
case HOOK_GAMEMODECHANGE:
break;
default:
HookScripts::InjectingHook(id); // inject hook to engine code HookScripts::InjectingHook(id); // inject hook to engine code
} }
}
} }
// run specific event procedure for all hook scripts // run specific event procedure for all hook scripts
@@ -171,10 +167,11 @@ void HookScripts::LoadHookScript(const char* name, int id) {
//if (id >= numHooks || IsGameScript(name)) return; //if (id >= numHooks || IsGameScript(name)) return;
bool hookIsLoaded = HookScripts::LoadHookScriptFile(name, id); bool hookIsLoaded = HookScripts::LoadHookScriptFile(name, id);
if (hookIsLoaded || (HookScripts::injectAllHooks && id != HOOK_SUBCOMBATDAMAGE)) { if (hookIsLoaded || injectHooks[id].injectState == 1 || (injectAllHooks && id != HOOK_SUBCOMBATDAMAGE)) {
HookScripts::InjectingHook(id); // inject hook to engine code HookScripts::InjectingHook(id); // inject hook to engine code
if (!hookIsLoaded) return; if (!hookIsLoaded) return; // only inject
HookFile hookFile = { id, name }; HookFile hookFile = { id, name };
HookScripts::hookScriptFilesList.push_back(hookFile); HookScripts::hookScriptFilesList.push_back(hookFile);
} }
@@ -258,8 +255,8 @@ void HookScripts::init() {
LoadGameHook::OnGameModeChange() += HookCommon::GameModeChangeHook; LoadGameHook::OnGameModeChange() += HookCommon::GameModeChangeHook;
LoadGameHook::OnAfterGameStarted() += SourceUseSkillOnInit; LoadGameHook::OnAfterGameStarted() += SourceUseSkillOnInit;
HookScripts::injectAllHooks = isDebug && (iniGetInt("Debugging", "InjectAllGameHooks", 0, ::sfall::ddrawIni) != 0); injectAllHooks = isDebug && (iniGetInt("Debugging", "InjectAllGameHooks", 0, ::sfall::ddrawIni) != 0);
if (HookScripts::injectAllHooks) dlogr("Injecting all game hooks", DL_HOOK|DL_INIT); if (injectAllHooks) dlogr("Injecting all game hooks", DL_HOOK|DL_INIT);
} }
} }
+1 -1
View File
@@ -71,6 +71,7 @@ enum HookType
HOOK_TARGETOBJECT = 42, HOOK_TARGETOBJECT = 42,
HOOK_ENCOUNTER = 43, HOOK_ENCOUNTER = 43,
HOOK_ADJUSTPOISON = 44, HOOK_ADJUSTPOISON = 44,
HOOK_ADJUSTRADS = 45,
HOOK_COUNT HOOK_COUNT
}; };
@@ -97,7 +98,6 @@ public:
static bool HookHasScript(int hookId); static bool HookHasScript(int hookId);
static bool injectAllHooks;
static void InjectingHook(int hookId); static void InjectingHook(int hookId);
static bool IsInjectHook(int hookId); static bool IsInjectHook(int hookId);
+39
View File
@@ -333,6 +333,39 @@ static void __declspec(naked) critter_adjust_poison_hack() {
} }
} }
static DWORD __fastcall AdjustRads_Script(DWORD critter, long amount) {
if (HookScripts::HookHasScript(HOOK_ADJUSTRADS) == false) return amount;
BeginHook();
argCount = 2;
args[0] = critter; // always dude
args[1] = amount;
RunHookScript(HOOK_ADJUSTRADS);
if (cRet) amount = rets[0];
EndHook();
return amount;
}
void __declspec(naked) critter_adjust_rads_hack() {
using namespace fo;
using namespace Fields;
__asm {
cmp dword ptr [eax + protoId], PID_Player; // critter.pid
jne isNotDude;
push ecx;
call AdjustRads_Script; // ecx - critter, edx - amount
pop ecx;
mov ebx, eax; // old/new amount
mov edx, ds:[FO_VAR_obj_dude];
xor eax, eax; // for continue func
isNotDude:
retn;
}
}
void Inject_UseObjOnHook() { void Inject_UseObjOnHook() {
HookCalls(UseObjOnHook, { 0x49C606, 0x473619 }); HookCalls(UseObjOnHook, { 0x49C606, 0x473619 });
@@ -374,6 +407,11 @@ void Inject_AdjustPoisonHook() {
MakeCall(0x42D21C, critter_adjust_poison_hack, 1); MakeCall(0x42D21C, critter_adjust_poison_hack, 1);
} }
void Inject_AdjustRadsHook() {
MakeCall(0x42D3B0, critter_adjust_rads_hack, 1);
SafeWrite16(0x42D3B6, 0xC085); // test eax, eax
}
void InitObjectHookScripts() { void InitObjectHookScripts() {
HookScripts::LoadHookScript("hs_useobjon", HOOK_USEOBJON); HookScripts::LoadHookScript("hs_useobjon", HOOK_USEOBJON);
HookScripts::LoadHookScript("hs_useobj", HOOK_USEOBJ); HookScripts::LoadHookScript("hs_useobj", HOOK_USEOBJ);
@@ -383,6 +421,7 @@ void InitObjectHookScripts() {
HookScripts::LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE); // combo hook HookScripts::LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE); // combo hook
HookScripts::LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE_END); HookScripts::LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE_END);
HookScripts::LoadHookScript("hs_adjustpoison", HOOK_ADJUSTPOISON); HookScripts::LoadHookScript("hs_adjustpoison", HOOK_ADJUSTPOISON);
HookScripts::LoadHookScript("hs_adjustrads", HOOK_ADJUSTRADS);
} }
} }
+1
View File
@@ -12,4 +12,5 @@ namespace sfall
void Inject_ScriptProcedureHook(); void Inject_ScriptProcedureHook();
void Inject_ScriptProcedureHook2(); void Inject_ScriptProcedureHook2();
void Inject_AdjustPoisonHook(); void Inject_AdjustPoisonHook();
void Inject_AdjustRadsHook();
} }
+4 -4
View File
@@ -138,7 +138,7 @@ long ScriptExtender::GetResetScriptReturnValue() {
return val; return val;
} }
static long FindOverrideSub(fo::Program* program) { static __forceinline long FindProgram(fo::Program* program) {
std::unordered_map<fo::Program*, SelfOverrideObj>::iterator overrideIt = selfOverrideMap.find(program); std::unordered_map<fo::Program*, SelfOverrideObj>::iterator overrideIt = selfOverrideMap.find(program);
if (overrideIt != selfOverrideMap.end()) { if (overrideIt != selfOverrideMap.end()) {
DWORD scriptId = overrideIt->second.object->scriptId; // script DWORD scriptId = overrideIt->second.object->scriptId; // script
@@ -167,11 +167,11 @@ static long FindOverrideSub(fo::Program* program) {
return -1; // change nothing return -1; // change nothing
} }
static long __fastcall FindOverride(fo::Program* program, fo::ScriptInstance* &script) { static long __fastcall FindOverride(fo::Program* program, fo::ScriptInstance** script) {
long result = FindOverrideSub(program); long result = FindProgram(program);
if (result == -2) { if (result == -2) {
if (script) { if (script) {
script = &overrideScript; // unsafe method! script may contain an incorrect address value in some engine functions *script = &overrideScript; // unsafe method! script may contain an incorrect address value in some engine functions
} else { } else {
result--; // set -3 result--; // set -3
} }
+3 -3
View File
@@ -407,11 +407,11 @@ void Skills::init() {
if (basedOnPoints) HookCall(0x4AA9EC, (void*)fo::funcoffs::skill_points_); // skill_dec_point_ if (basedOnPoints) HookCall(0x4AA9EC, (void*)fo::funcoffs::skill_points_); // skill_dec_point_
int tagBonus = iniGetInt("Skills", "TagSkillBonus", 20, file); int tagBonus = iniGetInt("Skills", "TagSkillBonus", 20, file);
if (tagBonus != 20 && tagBonus >=0 && tagBonus <= 100) SafeWrite8(0x4AA61E, static_cast<BYTE>(tagBonus)); if (tagBonus != 20 && tagBonus >=0 && tagBonus <= 100) SafeWrite8(0x4AA61E, static_cast<BYTE>(tagBonus)); // skill_level_
int tagMode = iniGetInt("Skills", "TagSkillMode", 0, file); int tagMode = iniGetInt("Skills", "TagSkillMode", 0, file);
if (tagMode & 1) SafeWrite8(0x4AA612, 0xEB); // 4th tag skill can have initial skill bonus (jz > jmp) if (tagMode & 1) SafeWrite8(0x4AA612, 0xEB); // 4th tag skill can have initial skill bonus. skill_level_ (jz > jmp)
if (tagMode & 2) SafeWrite16(0x4AA60E, 0x9090); // skill_level_ disables 2x skill points bonus for tag skills if (tagMode & 2) SafeWrite16(0x4AA60E, 0x9090); // disables double skill points bonus for tag skills. skill_level_
} }
} }