diff --git a/system/client-functions/BlueBurstExclusive/ServerEXPDisplay.59NJ.patch.s b/system/client-functions/BlueBurstExclusive/ServerEXPDisplay.59NJ.patch.s new file mode 100644 index 00000000..ef877019 --- /dev/null +++ b/system/client-functions/BlueBurstExclusive/ServerEXPDisplay.59NJ.patch.s @@ -0,0 +1,103 @@ +# This patch causes the client not to generate its own EXP text and instead use +# the EXP values generated by the server when showing the purple text for enemy +# deaths. This makes EXP gained via EXP share visible, as well as makes +# fractional EXP multiplers (in config.json) display properly. + +.meta name="Server EXP display" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start +start: + call install_hook + call apply_static_patches + ret + + + +install_hook: + pop ecx + push 0 # Write address instead of a call/jmp opcode + push 0x00A0DC54 + call get_code_size + .deltaof handle_6xBF_start, handle_6xBF_end +get_code_size: + pop eax + push dword [eax] + call handle_6xBF_end +handle_6xBF_start: # [std](G_6xBF* cmd @ [esp + 4]) -> void + mov edx, [esp + 4] + + mov ecx, [0x00A9A074] # local_client_id + cmp [edx + 2], cx + jne skip_text + + cmp byte [edx + 1], 3 + jl skip_text + movzx eax, word [edx + 8] # cmd.from_enemy_id + cmp eax, 0x1000 + jl skip_text + cmp eax, 0x1B50 + jge skip_text + call get_enemy_entity + + test eax, eax + jnz enemy_entity_ok + + # Use player entity if enemy entity is already gone + mov eax, 0x0068D618 + xchg eax, ecx + call ecx # eax = TObjPlayer::for_client_id(local_client_id); conveniently, this function preserves all regs except eax + +enemy_entity_ok: + push 0x0000FFFF # entity_id; ignored by TFontSmallTask if not a player + push dword [edx + 4] # amount = cmd.amount + push 0x00976380 # prefix = L"EXP" + push 0x14 + push 0x14 + push 0xFFFF00FF # color (ARGB) + add eax, 0x300 + push eax # position + mov eax, 0x0078B8E8 + call eax # TFontSmallTask___new__(...) + add esp, 0x1C + +skip_text: + mov eax, 0x0069292C # Original handle_6xBF + jmp eax # original_handle_6xBF(cmd) + +get_enemy_entity: + .include GetEnemyEntity-59NJ + ret + +handle_6xBF_end: + push ecx + .include WriteCallToCode-59NJ + + + +apply_static_patches: + .include WriteCodeBlocksBB + + .data 0x0078827D + .deltaof disable_kill_enemy_callsite_start, disable_kill_enemy_callsite_end +disable_kill_enemy_callsite_start: + nop + nop + nop + nop + nop +disable_kill_enemy_callsite_end: + + .data 0x00777381 + .deltaof disable_exp_steal_callsite_start, disable_exp_steal_callsite_end +disable_exp_steal_callsite_start: + add esp, 0x0C # Original function has `ret 0x0C` + nop + nop +disable_exp_steal_callsite_end: + + .data 0x00000000 + .data 0x00000000 diff --git a/system/client-functions/BlueBurstExclusive/StackLimits.59NL.patch.s b/system/client-functions/BlueBurstExclusive/StackLimits.5___.patch.s similarity index 91% rename from system/client-functions/BlueBurstExclusive/StackLimits.59NL.patch.s rename to system/client-functions/BlueBurstExclusive/StackLimits.5___.patch.s index e0757e21..86d49933 100644 --- a/system/client-functions/BlueBurstExclusive/StackLimits.59NL.patch.s +++ b/system/client-functions/BlueBurstExclusive/StackLimits.5___.patch.s @@ -10,6 +10,8 @@ .meta description="" .meta hide_from_patches_menu +.versions 59NJ 59NL + entry_ptr: reloc0: .offsetof start @@ -17,7 +19,7 @@ start: .include WriteCodeBlocksBB # Patch 1: rewrite item_is_stackable - .data 0x005C502C + .data .deltaof item_is_stackable_start, item_is_stackable_end item_is_stackable_start: @@ -32,7 +34,7 @@ item_is_stackable_start: push eax mov ecx, esp - .binary E8EC130100 # call max_stack_size_for_tool_start + .binary # call max_stack_size_for_tool_start pop ecx cmp eax, 1 jg return_1 @@ -48,7 +50,7 @@ return_1: item_is_stackable_end: # Patch 2: rewrite max_stack_size_for_tool - .data 0x005D6430 + .data .deltaof max_stack_size_for_tool_start, max_stack_size_for_tool_end max_stack_size_for_tool_start: diff --git a/system/client-functions/System/GetEnemyEntity-59NJ.x86.inc.s b/system/client-functions/System/GetEnemyEntity-59NJ.x86.inc.s new file mode 100644 index 00000000..a082ff82 --- /dev/null +++ b/system/client-functions/System/GetEnemyEntity-59NJ.x86.inc.s @@ -0,0 +1,44 @@ +# (uint16_t entity_id @ eax) -> TObjectV00b421c0* @ eax +# Preserves all registers except eax +get_enemy_entity: + push esi + push edi + push edx + push ecx + xor edx, edx + xchg edx, eax + cmp edx, 0x1000 + jl done + cmp edx, 0x4000 + jge done + + mov esi, [0x00AABCE8] # bs_low = next_player_entity_index + mov edi, [0x00AABCE4] + lea edi, [edi + esi - 1] # bs_high = next_player_entity_index + next_enemy_entity_index - 1 +bs_again: + cmp esi, edi + jge bs_done + lea ecx, [esi + edi] + shr ecx, 1 + mov eax, [ecx * 4 + 0x00AAB2A0] # all_entities[ecx] + cmp [eax + 0x1C], dx + jge bs_not_less + lea esi, [ecx + 1] + jmp bs_again +bs_not_less: + mov edi, ecx + jmp bs_again +bs_done: + + mov eax, [esi * 4 + 0x00AAB2A0] # all_entities[bs_low] + test eax, eax + je done + xor ecx, ecx + cmp [eax + 0x1C], dx + cmovne eax, ecx + +done: + pop ecx + pop edx + pop edi + pop esi diff --git a/system/client-functions/System/WriteAddressOfCode-59NJ.x86.inc.s b/system/client-functions/System/WriteAddressOfCode-59NJ.x86.inc.s new file mode 100644 index 00000000..78adec59 --- /dev/null +++ b/system/client-functions/System/WriteAddressOfCode-59NJ.x86.inc.s @@ -0,0 +1,42 @@ +# This file defines the following function: +# write_address_of_code( +# const void* patch_code, +# size_t patch_code_size, +# void** ptr_addr); +# This function allocates memory for patch_code, copies patch_code to that +# memory, then writes the address of the allocated code at the specified +# pointer. The allocated memory is never freed. +# This function pops its arguments off the stack before returning. + +write_call_to_code: + # [esp + 0x04] = code ptr + # [esp + 0x08] = code size + # [esp + 0x0C] = ptr addr + + # Allocate memory for the copied code + mov ecx, [0x00AA8F84] + push dword [esp + 0x08] + mov eax, 0x007A984C + call eax # malloc7 + test eax, eax + je done + + # Copy the code to the newly-allocated memory + # eax = dest pointer (from malloc7 call above) + mov edx, [esp + 0x04] # edx = source pointer + mov ecx, [esp + 0x08] # ecx = source size + push ebx +memcpy_again: + dec ecx + mov bl, [edx + ecx] # Copy one byte from source to dest + mov [eax + ecx], bl + test ecx, ecx + jne memcpy_again + pop ebx + + # Write the address + mov ecx, [esp + 0x0C] + mov [ecx], eax + +done: + ret 0x0C diff --git a/system/client-functions/System/WriteCallToCode-59NJ.x86.inc.s b/system/client-functions/System/WriteCallToCode-59NJ.x86.inc.s new file mode 100644 index 00000000..1dc8975d --- /dev/null +++ b/system/client-functions/System/WriteCallToCode-59NJ.x86.inc.s @@ -0,0 +1,76 @@ +# This file defines the following function: +# write_call_to_code( +# const void* patch_code, +# size_t patch_code_size, +# void* call_opcode_address, +# ssize_t call_opcode_bytes); +# This function allocates memory for patch_code, copies patch_code to that +# memory, then writes a call or jmp opcode to call_opcode_address that calls +# the code in the allocated memory region. The allocated memory is never freed. +# call_opcode_bytes specifies how many bytes at the callsite should be +# overwritten. This value must be at least 5; the first 5 bytes are overwritten +# with the call/jmp opcode itself; the rest are overwritten with nop opcodes. +# If call_opcode_bytes is positive, a call opcode is written; if it's negative, +# a jmp opcode is written. +# This function pops its arguments off the stack before returning. + +write_call_to_code: + # [esp + 0x04] = code ptr + # [esp + 0x08] = code size + # [esp + 0x0C] = jump callsite + # [esp + 0x10] = callsite size (if zero, write the address instead of a call) + + # Allocate memory for the copied code + mov ecx, [0x00AA8F84] + push dword [esp + 0x08] + mov eax, 0x007A984C + call eax # malloc7 + test eax, eax + je done + + # Copy the code to the newly-allocated memory + # eax = dest pointer (from malloc7 call above) + mov edx, [esp + 0x04] # edx = source pointer + mov ecx, [esp + 0x08] # ecx = source size + push ebx +memcpy_again: + dec ecx + mov bl, [edx + ecx] # Copy one byte from source to dest + mov [eax + ecx], bl + test ecx, ecx + jne memcpy_again + pop ebx + + mov edx, [esp + 0x0C] # edx = jump callsite + + # If the callsite size is zero, just write the address directly + cmp dword [esp + 0x10], 0 + jne write_call_or_jmp + mov [edx], eax + jmp done + + # Write the call or jmp opcode +write_call_or_jmp: + lea ecx, [eax - 5] + sub ecx, edx # ecx = (dest code addr) - (jump callsite) - 5 + cmp dword [esp + 0x10], 0 + setl al + or al, 0xE8 + mov [edx], al # Write E8 (call), or E9 (jmp) if size was negative + mov [edx + 1], ecx # Write delta + + # Write as many nops after the call opcode as necessary + mov ecx, 5 + mov eax, [esp + 0x10] + cmp eax, 0 + jge write_nop_again + neg eax +write_nop_again: + cmp ecx, eax + jge done + mov byte [edx + ecx], 0x90 + inc ecx + jmp write_nop_again + +done: + ret 0x10 diff --git a/system/client-functions/System/WriteCallToCodeMulti-59NJ.x86.inc.s b/system/client-functions/System/WriteCallToCodeMulti-59NJ.x86.inc.s new file mode 100644 index 00000000..1bcb838d --- /dev/null +++ b/system/client-functions/System/WriteCallToCodeMulti-59NJ.x86.inc.s @@ -0,0 +1,83 @@ +# This file defines the following function: +# void [/std] write_call_to_code( +# const void* patch_code @ [esp + 0x04], +# size_t patch_code_size @ [esp + 0x08], +# size_t call_count @ [esp + 0x0C], +# void* call_opcode_address @ [esp + 0x10], +# ssize_t call_opcode_bytes @ [esp + 0x14], +# ...); +# This function allocates memory for patch_code, copies patch_code to that +# memory, then writes a call or jmp opcode to call_opcode_address that calls +# the code in the allocated memory region. The allocated memory is never freed. +# call_opcode_bytes specifies how many bytes at the callsite should be +# overwritten. This value must be at least 5; the first 5 bytes are overwritten +# with the call/jmp opcode itself; the rest are overwritten with nop opcodes. +# This function pops its arguments off the stack before returning (including +# all the varargs). + +write_call_to_code: + # [esp + 0x04] = code ptr + # [esp + 0x08] = code size + # [esp + 0x0C] = callsite count + # [esp + 0x10] = callsite address + # [esp + 0x14] = callsite size + # ... (further callsite address/size pairs) + + # Allocate memory for the copied code + mov ecx, [0x00AA8F84] + push dword [esp + 0x08] + mov eax, 0x007A984C + call eax # malloc7 + test eax, eax + je done + + # Copy the code to the newly-allocated memory + # eax = dest pointer (from malloc7 call above) + mov edx, [esp + 0x04] # edx = source pointer + mov ecx, [esp + 0x08] # ecx = source size + push ebx +memcpy_again: + dec ecx + mov bl, [edx + ecx] # Copy one byte from source to dest + mov [eax + ecx], bl + test ecx, ecx + jne memcpy_again + pop ebx + + # Write the call opcodes + xchg ebx, [esp + 0x0C] # Save ebx; get callsite count + mov [esp - 0x08], esi + mov [esp - 0x0C], eax + mov esi, 0x10 # Stack offset of first callsite pair + +next_callsite: + mov edx, [esp + esi] # edx = jump callsite + lea ecx, [eax - 5] + sub ecx, edx # ecx = (dest code addr) - (jump callsite) - 5 + mov byte [edx], 0xE8 + mov [edx + 1], ecx # Write E8 (call) followed by delta + + # Write as many nops after the call opcode as necessary + mov ecx, 5 + mov eax, [esp + esi + 4] +write_nop_again: + cmp ecx, eax + jge this_callsite_done + mov byte [edx + ecx], 0x90 + inc ecx + jmp write_nop_again + +this_callsite_done: + mov eax, [esp - 0x0C] + add esi, 8 + dec ebx + jnz next_callsite + + mov ecx, esi + mov ebx, [esp + 0x0C] + mov esi, [esp - 0x08] + +done: + mov eax, [esp] + add esp, ecx + jmp eax