Taskbar: 7 weapon sets and 7 combat-art slots
cUI_Taskbar2's slot arrays move behind the (enlarged) object with room for 7 each; all instructions addressing them, the relative accesses between the two arrays, the slot loops and the count clamp are patched from tables in game/addresses.h (belt loops untouched). Slots 6 / 7 unlock at levels 45 / 65; AssignArtToSlot accepts slots up to 6. Slot keys call the taskbar's selection directly (honouring COMBINE_SLOTS). Shared code: re::PatchAll / PatchInstructionByte for patch tables, game/engine/objects.h for object, RTTI and kernel calls, slot-hook templates in skillslots.cpp. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
998e315d98
commit
dbc632750b
12 files changed
+364
-145
No files matched your search
@@ -51,6 +51,32 @@ bool PatchInstructionDword(uint32_t instruction, uint32_t from, uint32_t to) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PatchInstructionByte(uint32_t instruction, size_t offset, uint8_t from, uint8_t to) {
|
||||
const auto* code = reinterpret_cast<const uint8_t*>(static_cast<uintptr_t>(instruction));
|
||||
if (code[offset] != from) {
|
||||
Log("patch 0x%08x: expected byte 0x%02x at +%u, found 0x%02x, skipped", instruction, from,
|
||||
static_cast<unsigned>(offset), code[offset]);
|
||||
return false;
|
||||
}
|
||||
return WriteCode(instruction + static_cast<uint32_t>(offset), &to, 1);
|
||||
}
|
||||
|
||||
int PatchAll(const BytePatch* patches, size_t count, uint8_t to) {
|
||||
int failed = 0;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
failed += !PatchInstructionByte(patches[i].instruction, patches[i].offset, patches[i].from, to);
|
||||
}
|
||||
return failed;
|
||||
}
|
||||
|
||||
int PatchAll(const uint32_t* instructions, size_t count, uint32_t from, uint32_t to) {
|
||||
int failed = 0;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
failed += !PatchInstructionDword(instructions[i], from, to);
|
||||
}
|
||||
return failed;
|
||||
}
|
||||
|
||||
bool RedirectOperand(uint32_t instruction, uint32_t from, const void* to) {
|
||||
return PatchInstructionDword(instruction, from, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(to)));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,29 @@ bool RedirectOperand(uint32_t instruction, uint32_t from, const void* to);
|
||||
// `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);
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
Reference in new issue
Block a user