Files
psopeeps-newserv/system/client-functions/EnemyDamageSync/EnemyDamageSync.4___.patch.s
T
2025-10-26 21:13:20 -07:00

357 lines
14 KiB
ArmAsm

.meta name="DMC"
.meta description="Mitigates effects\nof enemy health\ndesync"
.meta client_flag="0x2000000000000000"
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
entry_ptr:
reloc0:
.offsetof start
write_call_to_code_multi:
.include WriteCallToCodeMultiXB
start:
call write_static_patches
call write_incr_hp_with_sync
call write_6x0A_patch
call write_6xE4_handler
ret
call_write_call_to_code:
call write_call_to_code_multi
ret
write_6xE4_handler:
push 0 # Absolute address, not call opcode
push <VERS 0x00537184 0x00537804 0x0053EB24 0x0053BFA4 0x0053B844 0x0053BFA4 0x0053C344>
push 1
call +4
.deltaof handle_6xE4, handle_6xE4_end
pop eax
push dword [eax]
call call_write_call_to_code
handle_6xE4: # [std] (G_6xE4* cmd @ [esp + 4]) -> void
push ebx
push esi
push edi
test byte [<VERS 0x00630590 0x00630BF0 0x00638738 0x00635C20 0x006354B8 0x00635C20 0x00635FB8>], 0x80
jz handle_6xE4_return
mov ebx, [esp + 0x10] # cmd
movzx eax, word [ebx + 2]
cmp eax, 0x1000
jl handle_6xE4_return
cmp eax, 0x1B50
jge handle_6xE4_return
mov edi, eax
mov eax, <VERS 0x002B36B0 0x002B4180 0x002B5710 0x002B5220 0x002B5400 0x002B5240 0x002B5510>
call eax # TObjEnemy* ene = get_enemy_entity(cmd->header.entity_id);
push eax
movzx eax, word [ebx + 2]
and eax, 0x0FFF
imul eax, eax, 0x0C
add eax, [<VERS 0x00633068 0x006336C8 0x0063B210 0x006386F8 0x00637F90 0x006386F8 0x00638A90>] # eax = state_for_enemy(cmd->header.entity_id)
cmp dword [ebx + 0x0C], 0
jl handle_6xE4_not_proportional
mov cx, [ebx + 0x0A] # cmd->max_hp
sub cx, [eax + 0x06] # st.total_damage
movzx ecx, cx
xor edx, edx
cmp ecx, edx
cmovl ecx, edx
push ecx
fild st0, dword [esp] # current_hp = static_cast<float>(max<int32_t>(cmd->max_hp - st.total_damage, 0))
fld st0, dword [ebx + 0x0C]
fmulp st1, st0
fistp dword [esp], st0
mov ecx, dword [esp] # adjusted_hit_amount = static_cast<int16_t>(current_hp * cmd->factor)
add esp, 4
xor edx, edx
inc edx
cmp ecx, edx
cmovl ecx, edx
mov [ebx + 0x04], cx # cmd->hit_amount = min<int32_t>(1, adjusted_hit_amount)
handle_6xE4_not_proportional:
movzx edx, word [eax + 0x06] # st.total_damage
movsx esi, word [ebx + 0x04] # cmd->hit_amount
movzx edi, word [ebx + 0x0A] # cmd->max_hp
add edx, esi # st.total_damage + cmd->hit_amount
cmp edx, edi
jl handle_6xE4_damage_less_than_max_hp
mov [eax + 0x06], di # st.total_damage = cmd->max_hp;
mov edx, [eax]
test edx, 0x800
jnz handle_6xE4_return_pop_ene
or edx, 0x800
mov [eax], edx
cmp dword [esp], 0
je handle_6xE4_return_pop_ene
push edx # out_cmd.flags
sub esp, 8
mov word [esp], 0x030A # out_cmd.header.{subcommand,size}
mov si, [ebx + 2]
mov [esp + 2], si # out_cmd.header.entity_id
and si, 0x0FFF
mov [esp + 4], si # out_cmd.entity_index
mov [esp + 6], di # out_cmd.total_damage
mov ecx, esp
push ecx # For handle_60 later
mov ebx, [<VERS 0x0071EEFC 0x0071F55C 0x007270A0 0x0072459C 0x00723E20 0x0072459C 0x00724920>] # root_protocol
test ebx, ebx
jz handle_6xE4_root_protocol_missing
mov eax, 0x0C
mov edx, <VERS 0x002DA120 0x002DACF0 0x002DC5B0 0x002DC080 0x002DC580 0x002DC0B0 0x002DC600>
call edx # send_60(root_protocol, &out_cmd, sizeof(out_cmd))
handle_6xE4_root_protocol_missing:
mov dword [<VERS 0x0071E8C8 0x0071EF28 0x00726A68 0x00723F68 0x007237E8 0x00723F68 0x007242E8>], 1
mov eax, <VERS 0x002DBC30 0x002DC7B0 0x002DE070 0x002DDB90 0x002DE090 0x002DDBC0 0x002DE0C0>
call eax # handle_60(&out_cmd)
mov dword [<VERS 0x0071E8C8 0x0071EF28 0x00726A68 0x00723F68 0x007237E8 0x00723F68 0x007242E8>], 0
add esp, 0x14
jmp handle_6xE4_return
handle_6xE4_damage_less_than_max_hp:
xor edi, edi
cmp edx, edx
cmovl edx, edi
mov [eax + 0x06], dx # st.total_damage = std::max<int16_t>(st.total_damage + cmd->hit_amount, 0);
mov esi, eax # esi = ene_st
mov eax, [esp] # eax = ene
test eax, eax
jz handle_6xE4_return_pop_ene
mov ecx, eax
push esi
mov edx, [ecx]
call [edx + 0x138] # ene->vtable[0x4E](ene, &st);
handle_6xE4_return_pop_ene:
add esp, 4
handle_6xE4_return:
pop edi
pop esi
pop ebx
ret
handle_6xE4_end:
write_6x0A_patch:
push 5
push <VERS 0x002B3B55 0x002B4625 0x002B5BB5 0x002B56C5 0x002B58A5 0x002B56E5 0x002B59B5>
push 1
call +4
.deltaof on_6x0A_patch_start, on_6x0A_patch_end
pop eax
push dword [eax]
call call_write_call_to_code
on_6x0A_patch_start: # (TObjectV004434c8* this @ eax, int16_t amount @ cx) -> bool @ eax
test byte [<VERS 0x00630590 0x00630BF0 0x00638738 0x00635C20 0x006354B8 0x00635C20 0x00635FB8>], 0x80
jnz on_6x0A_patch_skip_write
mov [esp + 0x16], ax
on_6x0A_patch_skip_write:
ret
on_6x0A_patch_end:
# Write TObjectV004434c8::incr_hp_with_sync
write_incr_hp_with_sync:
push 5
push <VERS 0x002A60CF 0x002A6BAF 0x002A807F 0x002A7B0F 0x002A7CEF 0x002A7B2F 0x002A7DAF> # v17 inlined callsite + 5
push 5
push <VERS 0x002A808D 0x002A8B6D 0x002AA03D 0x002A9ACD 0x002A9CAD 0x002A9AED 0x002A9D6D> # TObjectV004434c8::subtract_hp_if_not_in_state_2 + D
push 5
push <VERS 0x002A68FB 0x002A73DB 0x002A88AB 0x002A833B 0x002A851B 0x002A835B 0x002A85DB> # TObjectV004434c8::v18_accept_hit (presumably Resta) - this is add_hp, not subtract_hp!
push 5
push <VERS 0x002A650D 0x002A6FED 0x002A84BD 0x002A7F4D 0x002A812D 0x002A7F6D 0x002A81ED> # TObjectV004434c8::v18_accept_hit cases 0 and 4
push 5
push <VERS 0x002A65BA 0x002A709A 0x002A856A 0x002A7FFA 0x002A81DA 0x002A801A 0x002A829A> # TObjectV004434c8::v18_accept_hit case 1
push 5
push <VERS 0x002A6670 0x002A7150 0x002A8620 0x002A80B0 0x002A8290 0x002A80D0 0x002A8350> # TObjectV004434c8::v18_accept_hit case 2
push 5
push <VERS 0x002A6769 0x002A7249 0x002A8719 0x002A81A9 0x002A8389 0x002A81C9 0x002A8449> # TObjectV004434c8::v18_accept_hit case 3
push 5
push <VERS 0x002A6C19 0x002A76F9 0x002A8BC9 0x002A8659 0x002A8839 0x002A8679 0x002A88F9> # TObjectV004434c8::v18_accept_hit case 0x13
push 5
push <VERS 0x002A6CAC 0x002A778C 0x002A8C5C 0x002A86EC 0x002A88CC 0x002A870C 0x002A898C> # TObjectV004434c8::v18_accept_hit case 0x15
push 5
push <VERS 0x002A70AE 0x002A7B92 0x002A9062 0x002A8AF2 0x002A8CD2 0x002A8B12 0x002A8D92> # TObjectV004434c8::v19_handle_hit_special_effects case 1
push 5
push <VERS 0x002A7A53 0x002A7BD3 0x002A90A3 0x002A8B33 0x002A8D13 0x002A8B53 0x002A8DD3> # TObjectV004434c8::v19_handle_hit_special_effects case 1
push 5
push <VERS 0x002A76C4 0x002A81A8 0x002A9678 0x002A9108 0x002A92E8 0x002A9128 0x002A93A8> # TObjectV004434c8::v19_handle_hit_special_effects case 6
push 5
push <VERS 0x002A7953 0x002A8437 0x002A9907 0x002A9397 0x002A9577 0x002A93B7 0x002A9637> # TObjectV004434c8::v19_handle_hit_special_effects case 9
push 5
push <VERS 0x0024A140 0x002A8530 0x002A9A00 0x002A9490 0x002A9670 0x002A94B0 0x002A9730> # TObjectV004434c8::v19_handle_hit_special_effects case 0x0A
push 5
push <VERS 0x002A7CDB 0x002A87BF 0x002A9C8F 0x002A971F 0x002A98FF 0x002A973F 0x002A99BF> # TObjectV004434c8::v19_handle_hit_special_effects case 0x0D
push 15
call +4
.deltaof on_add_or_subtract_hp_start, on_add_or_subtract_hp_end
pop eax
push dword [eax]
call call_write_call_to_code
on_add_or_subtract_hp_start: # (TObjectV004434c8* this @ eax, int16_t amount @ cx) -> bool @ eax
# Check if callsite is subtract_hp_if_not_in_state_2
push eax
push ecx
push ebx
test byte [<VERS 0x00630590 0x00630BF0 0x00638738 0x00635C20 0x006354B8 0x00635C20 0x00635FB8>], 0x80
jz on_add_or_subtract_hp_skip_send
movzx edx, word [eax + 0x1C] # ene->entity_id
cmp edx, 0x1000
jl on_add_or_subtract_hp_skip_send
cmp edx, 0x1B50
jge on_add_or_subtract_hp_skip_send
and edx, 0x0FFF
imul edx, edx, 0x0C
add edx, [<VERS 0x00633068 0x006336C8 0x0063B210 0x006386F8 0x00637F90 0x006386F8 0x00638A90>] # eax = state_for_enemy(cmd->header.entity_id)
sub esp, 0x10
mov word [esp], 0x04E4
mov bx, [eax + 0x1C]
mov [esp + 0x02], bx # cmd.entity_id
cmp dword [esp + 0x1C], <VERS 0x002A6900 0x002A73E0 0x002A88B0 0x002A8340 0x002A8520 0x002A8360 0x002A85E0> # Check if callsite is add_hp
jne on_add_or_subtract_hp_skip_negate_amount
neg cx
on_add_or_subtract_hp_skip_negate_amount:
mov [esp + 0x04], cx # cmd.hit_amount
mov bx, [edx + 6]
mov [esp + 0x06], bx # cmd.total_damage_before_hit
mov bx, [eax + 0x0330]
mov [esp + 0x08], bx # cmd.current_hp
mov bx, [eax + 0x02BC]
mov [esp + 0x0A], bx # cmd.max_hp
mov dword [esp + 0x0C], 0xBF800000 # cmd.factor
cmp dword [esp + 0x1C], <VERS 0x002A7CE0 0x002A87C4 0x002A9C94 0x002A9724 0x002A9904 0x002A9744 0x002A99C4> # Check if callsite is Devil's/Demon's
jne on_add_or_subtract_hp_not_proportional
# esp is 0x20 down from where it is in caller's context
mov cx, 100
sub cx, [esp + 0x34] # cx = (100 - special_amount)
movsx ecx, cx
push ecx
fild st0, dword [esp] # current_hp_factor = static_cast<float>(100 - special_amount)
fmul st0, dword [esp + 0x3C] # *= weapon_reduction_factor
mov dword [esp], 0x42C80000 # 100.0f
fdiv st0, dword [esp]
add esp, 4
fstp dword [esp + 0x0C], st0 # cmd.factor = ((100 - special_amount) * weapon_reduction_factor) / 100
on_add_or_subtract_hp_not_proportional:
mov ecx, esp
mov ebx, [<VERS 0x0071EEFC 0x0071F55C 0x007270A0 0x0072459C 0x00723E20 0x0072459C 0x00724920>] # root_protocol
test ebx, ebx
jz on_add_or_subtract_hp_skip_send
mov eax, 0x10
# Can't just `call <addr>` here because this code is relocated at apply time
mov edx, <VERS 0x002DA120 0x002DACF0 0x002DC5B0 0x002DC080 0x002DC580 0x002DC0B0 0x002DC600>
call edx # send_60(root_protocol, &out_cmd, sizeof(out_cmd))
add esp, 0x10
on_add_or_subtract_hp_skip_send:
mov edx, <VERS 0x002A80C0 0x002A8BA0 0x002AA070 0x002A9B00 0x002A9CE0 0x002A9B20 0x002A9DA0> # subtract_hp
mov eax, <VERS 0x002A80F0 0x002A8BD0 0x002AA0A0 0x002A9B30 0x002A9D10 0x002A9B50 0x002A9DD0> # add_hp
cmp dword [esp + 0x0C], <VERS 0x002A6900 0x002A73E0 0x002A88B0 0x002A8340 0x002A8520 0x002A8360 0x002A85E0> # Check if callsite is add_hp
cmove edx, eax
pop ebx
pop ecx
pop eax
jmp edx
on_add_or_subtract_hp_end:
write_static_patches:
.include WriteCodeBlocksXB
.data <VERS 0x002DB7A0 0x002DC370 0x002DDC30 0x002DD700 0x002DDC00 0x002DD730 0x002DDC80>
.data 9
flag_check_start:
test byte [<VERS 0x00630590 0x00630BF0 0x00638738 0x00635C20 0x006354B8 0x00635C20 0x00635FB8>], 0x01
jz +0x38
flag_check_end:
# Replace 6x09 handler with 6xE4
.data <VERS 0x00537180 0x00537800 0x0053EB20 0x0053BFA0 0x0053B840 0x0053BFA0 0x0053C340>
.data 4
.data 0x000600E4 # subcommand=0xE4, flags=6
# Handler address written by write_6xE4_handler
# Rewrite TObjectV004434c8::subtract_hp_if_not_in_state_2
.data <VERS 0x002A8080 0x002A8B60 0x002AA030 0x002A9AC0 0x002A9CA0 0x002A9AE0 0x002A9D60>
.deltaof on_subtract_hp_if_not_in_state_2_start, on_subtract_hp_if_not_in_state_2_end
.address <VERS 0x002A8080 0x002A8B60 0x002AA030 0x002A9AC0 0x002A9CA0 0x002A9AE0 0x002A9D60>
on_subtract_hp_if_not_in_state_2_start: # (TObjectV004434c8* this @ eax, int16_t amount @ cx) -> bool @ eax
cmp word [eax + 0x328], 2
jne on_subtract_hp_if_not_in_state_2_do_subtract
xor eax, eax
ret
on_subtract_hp_if_not_in_state_2_do_subtract:
call -1 # Overwritten by write_call_to_code_multi later
ret
on_subtract_hp_if_not_in_state_2_end:
# Inlined callsite of subtract_hp in TObjectV004434c8::v17
.data <VERS 0x002A60CA 0x002A6BAA 0x002A807A 0x002A7B0A 0x002A7CEA 0x002A7B2A 0x002A7DAA>
.deltaof v17_subtract_hp_inlined_callsite_start, v17_subtract_hp_inlined_callsite_end
.address <VERS 0x002A60CA 0x002A6BAA 0x002A807A 0x002A7B0A 0x002A7CEA 0x002A7B2A 0x002A7DAA>
v17_subtract_hp_inlined_callsite_start:
# This must assemble to exactly 0x1A bytes. There is a vfn call shortly after
# this, and fortunately it appears eax, ecx, and edx are not used before
# then, so we don't have to save any registers here; we just have to move the
# args into the right places.
mov cx, ax
mov eax, edi
call -1 # Overwritten by write_call_to_code_multi later
jmp v17_subtract_hp_inlined_callsite_end
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
v17_subtract_hp_inlined_callsite_end:
.data 0x00000000
.data 0x00000000