- Skill bar: no art selection; slot 0 is the right-click art, keys 0-6 cast slots directly; cooldown labels in exact seconds; slot 0 set apart. - Arts regenerate on their own (casting no longer refills the group). - Auto-attack after targeting or casting at a hostile creature, at the speed of a held button. - Auras can be recast while active (renew, no second visual). - Blicke wie Klingen hits every enemy in range, physical, with life leech. - Blitzschlag steers the game's own chain: 3 + level/5 hits, 1 + level/10 chains, re-hits a lone enemy. - Weapon sets: click selects, drag only from the selected set; the leftmost set is clickable (signed hit tests). - Esc closes open windows before opening the game menu. - HUD: standalone buttons and texts are centred. - Record cache sized for widened views (town lag while walking). - Shared game code: orders, fx events, text drawing, cooldowns, effects. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
81 lines
2.9 KiB
C++
81 lines
2.9 KiB
C++
#pragma once
|
|
// Verified in-place patches of sacred.exe code. Every function checks that the
|
|
// bytes it expects are present before writing, logs and refuses otherwise.
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
namespace re {
|
|
|
|
bool WriteCode(uint32_t address, const void* bytes, size_t size);
|
|
|
|
// The instruction at `instruction` has the absolute memory operand `from`
|
|
// (e.g. `fmul st, dword ptr [from]`); make it read `to` instead.
|
|
bool RedirectOperand(uint32_t instruction, uint32_t from, const void* to);
|
|
|
|
// Replace a 32-bit displacement/immediate `from` inside the instruction at
|
|
// `instruction` by `to` (e.g. a struct field offset in `[ebp+ecx*8+3FF2Ch]`).
|
|
bool PatchInstructionDword(uint32_t instruction, uint32_t from, uint32_t to);
|
|
|
|
// Replace the byte `from` at `instruction + offset` (an imm8 or disp8) by `to`.
|
|
bool PatchInstructionByte(uint32_t instruction, size_t offset, uint8_t from, uint8_t to);
|
|
|
|
// Replace the code bytes `from` at `address` by `to` (same length), e.g. a
|
|
// whole short instruction.
|
|
bool ReplaceCode(uint32_t address, const void* from, const void* to, size_t size);
|
|
|
|
// The `call rel32` at `instruction` (E8) calls `from`; make it call `to`.
|
|
bool RedirectCall(uint32_t instruction, uint32_t from, const void* to);
|
|
|
|
// The near conditional jump at `instruction` (0F 8x rel32) goes to `from`;
|
|
// make it go to `to`.
|
|
bool RedirectJcc(uint32_t instruction, uint32_t from, const void* to);
|
|
|
|
// A code replacement, for tables.
|
|
struct CodeReplacement {
|
|
uint32_t address;
|
|
uint8_t from[8];
|
|
uint8_t to[8];
|
|
uint8_t size;
|
|
};
|
|
int ReplaceAll(const CodeReplacement* replacements, size_t count);
|
|
template <size_t N>
|
|
int ReplaceAll(const CodeReplacement (&replacements)[N]) {
|
|
return ReplaceAll(replacements, N);
|
|
}
|
|
|
|
// One byte operand of an instruction, for tables of byte patches.
|
|
struct BytePatch {
|
|
uint32_t instruction;
|
|
uint8_t offset; // of the byte within the instruction
|
|
uint8_t from;
|
|
};
|
|
|
|
// Apply the same change to a table of instructions; return the number that
|
|
// failed (each failure is logged).
|
|
int PatchAll(const BytePatch* patches, size_t count, uint8_t to);
|
|
int PatchAll(const uint32_t* instructions, size_t count, uint32_t from, uint32_t to);
|
|
template <size_t N>
|
|
int PatchAll(const BytePatch (&patches)[N], uint8_t to) {
|
|
return PatchAll(patches, N, to);
|
|
}
|
|
template <size_t N>
|
|
int PatchAll(const uint32_t (&instructions)[N], uint32_t from, uint32_t to) {
|
|
return PatchAll(instructions, N, from, to);
|
|
}
|
|
|
|
// `push imm32` (68 xx xx xx xx) at `instruction`: replace `expected` by `value`.
|
|
bool PatchPushImm32(uint32_t instruction, uint32_t expected, uint32_t value);
|
|
|
|
// Code that must run once at startup, after the hooks are installed.
|
|
class StartupRegistration {
|
|
public:
|
|
StartupRegistration(const char* name, void (*function)());
|
|
};
|
|
void RunStartupFunctions();
|
|
|
|
} // namespace re
|
|
|
|
#define RE_STARTUP(name, function) \
|
|
static ::re::StartupRegistration name##_startup(#name, &function)
|