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:
khallaandClaude Opus 5.5 committed 2026-10-15 00:38:34 +02:00
1 parent 998e315d98
commit dbc632750b
12 files changed
+364 -145

No files matched your search

+26
View File
@@ -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)));
}
+23
View File
@@ -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);