Files
Perfectium/util.asm
T

402 lines
13 KiB
NASM

INCLUDE include\master.inc
EXTRN GetModuleHandleA :PROC
EXTRN sinf :PROC
EXTRN cosf :PROC
EXTRN atan2f :PROC
EXTRN rand :PROC
EXTRN strtoul :PROC
.const
; DIVIDE_BY_2 = PI / 360.0f (used for Rotator.component * PI/360 in RotToQuat)
fDivBy2 REAL4 0.00872664625r ; PI / (180 * 2)
fRadToDeg REAL4 57.2957795r ; 180 / PI
fRandMax REAL4 32767.0r ; RAND_MAX (MSVC)
.code
; Utils_FindPattern(RCX=pszPattern, EDX=bRelative, R8D=offset) -> RAX
;
; Frame: 7 callee-save pushes (rbp rbx rsi rdi r12 r13 r14) + sub 576 bytes.
; After 7 pushes, RSP = 0 mod 16. 576 mod 16 = 0 -> RSP = 0 at every CALL.
;
; Frame layout (offsets from RSP after sub):
; [rsp+ 0 .. 31] shadow space for callees
; [rsp+ 32 .. 543] pat_ints[128] (512 bytes, int32 per element)
; [rsp+544] pat_len DWORD
; [rsp+548] bRelative saved DWORD
; [rsp+552] offset_val saved DWORD
; [rsp+556] SizeOfImage saved DWORD
; [rsp+560..567] endptr QWORD (for strtoul)
; [rsp+568..575] pad
Utils_FindPattern PROC
push rbp
push rbx
push rsi
push rdi
push r12
push r13
push r14
sub rsp, 576
mov r12, rcx ; r12 = pszPattern (parse cursor)
mov DWORD PTR [rsp + 548], edx ; bRelative
mov DWORD PTR [rsp + 552], r8d ; offset
xor ecx, ecx
call GetModuleHandleA ; RAX = module base
mov r13, rax ; r13 = base address
; IMAGE_DOS_HEADER.e_lfanew is a DWORD at [base + 0x3C]
movsxd rbx, DWORD PTR [r13 + 3Ch] ; rbx = e_lfanew
add rbx, r13 ; rbx = IMAGE_NT_HEADERS64*
; IMAGE_NT_HEADERS64: Signature(4) + FileHeader(20) = 0x18 to OptionalHeader
; IMAGE_OPTIONAL_HEADER64.SizeOfImage: DWORD at OptionalHeader+0x38 -> NtHdrs+0x50
mov edi, DWORD PTR [rbx + 50h] ; edi = SizeOfImage
mov DWORD PTR [rsp + 556], edi ; save SizeOfImage
; r12 = current char pointer into pszPattern
; rsi = write index (0-based, < 128)
xor esi, esi ; rsi = pattern element count
@@parse_top:
movzx eax, BYTE PTR [r12]
test al, al
jz @@parse_done ; end of string
cmp al, ' ' ; skip spaces
jne @@parse_not_space
inc r12
jmp @@parse_top
@@parse_not_space:
cmp al, '?' ; wildcard?
jne @@parse_hex
; Wildcard: store -1
mov DWORD PTR [rsp + 32 + rsi*4], -1
inc esi
inc r12 ; skip first '?'
cmp BYTE PTR [r12], '?'
jne @@parse_top
inc r12 ; skip optional second '?'
jmp @@parse_top
@@parse_hex:
; Parse two hex digits via strtoul(r12, &endptr, 16)
; strtoul will consume "AB" and set *endptr to the char after
mov rcx, r12 ; arg1: string ptr
lea rdx, [rsp + 560] ; arg2: &endptr
mov r8d, 16 ; arg3: base
call strtoul ; EAX = byte value
movsxd r14, eax ; zero-extend byte to 64-bit (safe: 0..255)
mov DWORD PTR [rsp + 32 + rsi*4], r14d ; pat_ints[rsi] = byte value
inc esi
mov r12, QWORD PTR [rsp + 560] ; r12 = endptr (now points at space/end)
jmp @@parse_top
@@parse_done:
mov DWORD PTR [rsp + 544], esi ; pat_len = esi
; Guard: if pat_len == 0, no scan
test esi, esi
jz @@not_found
; Outer loop: i from 0 to SizeOfImage - pat_len (inclusive)
mov r14d, DWORD PTR [rsp + 556] ; r14d = SizeOfImage
sub r14d, esi ; r14d = SizeOfImage - pat_len
js @@not_found ; underflow -> image too small
; r12d = pat_len (repurpose r12 - pszPattern no longer needed)
mov r12d, esi ; r12d = pat_len
xor esi, esi ; esi = i (outer counter)
; rbx = &pat_ints[0] = rsp + 32
lea rbx, [rsp + 32]
@@outer_loop:
cmp esi, r14d ; i > SizeOfImage - pat_len?
jg @@not_found
; Inner loop: j from 0 to pat_len-1
xor r9d, r9d ; r9d = j
@@inner_loop:
cmp r9d, r12d ; j >= pat_len?
jge @@found ; all bytes matched
; Load pat_ints[j] (int32: -1 or byte 0..255)
movsxd rdi, r9d
mov eax, DWORD PTR [rbx + rdi*4] ; eax = pat_ints[j]
cmp eax, -1
je @@wildcard ; skip wildcard byte
; Compare scan byte: base[i+j] vs pat_ints[j]
movsxd rdi, esi ; rdi = i
add rdi, r9 ; rdi = i + j
movzx edx, BYTE PTR [r13 + rdi] ; edx = scan byte
cmp edx, eax
jne @@mismatch
@@wildcard:
inc r9d
jmp @@inner_loop
@@mismatch:
inc esi
jmp @@outer_loop
@@found:
; Compute match address = base + i
movsxd rax, esi
add rax, r13
cmp DWORD PTR [rsp + 548], 0
je @@done
; address = address + offset + 4 + *(int32*)(address + offset)
movsxd rcx, DWORD PTR [rsp + 552] ; rcx = offset
movsxd rdx, DWORD PTR [rax + rcx] ; rdx = *(int32*)(address + offset)
lea rax, [rax + rcx + 4] ; rax = address + offset + 4
add rax, rdx ; rax += displacement
jmp @@done
@@not_found:
xor eax, eax ; return NULL
@@done:
add rsp, 576
pop r14
pop r13
pop r12
pop rdi
pop rsi
pop rbx
pop rbp
ret
Utils_FindPattern ENDP
; Utils_sinCos(RCX=float* ScalarSin, RDX=float* ScalarCos, XMM2=float Value)
; Computes sin and cos of Value via CRT sinf/cosf.
; Stores results at *ScalarSin and *ScalarCos.
; Frame: push rbp rbx rsi + sub 48
; 3 pushes -> RSP = 0 mod 16. sub 48 (48=0) -> 0
; Local [rsp+32]: saved Value REAL4 (XMM2 is volatile across calls)
Utils_sinCos PROC
push rbp
push rbx
push rsi
sub rsp, 48
mov rbx, rcx ; rbx = ScalarSin ptr
mov rsi, rdx ; rsi = ScalarCos ptr
movss DWORD PTR [rsp + 32], xmm2 ; save Value (XMM2 volatile across calls)
; sin(Value)
movss xmm0, DWORD PTR [rsp + 32]
call sinf ; XMM0 = sin(Value)
movss DWORD PTR [rbx], xmm0 ; *ScalarSin = sin
; cos(Value)
movss xmm0, DWORD PTR [rsp + 32]
call cosf ; XMM0 = cos(Value)
movss DWORD PTR [rsi], xmm0 ; *ScalarCos = cos
add rsp, 48
pop rsi
pop rbx
pop rbp
ret
Utils_sinCos ENDP
; Utils_RotToQuat(RCX=FRotator* rot, RDX=FQuat* out_quat)
; Converts Pitch/Yaw/Roll (degrees) to quaternion X/Y/Z/W.
; FRotator layout: Pitch[+0] Yaw[+4] Roll[+8] (all REAL4)
; FQuat layout: X[+0] Y[+4] Z[+8] W[+12] (all REAL4)
; Frame: push rbp rbx rsi + sub 64
; 3 pushes (RSP=0). sub 64 (64=0) -> 0
; Locals:
; [rsp+32] SP [rsp+36] CP [rsp+40] SY
; [rsp+44] CY [rsp+48] SR [rsp+52] CR
Utils_RotToQuat PROC
push rbp
push rbx
push rsi
sub rsp, 64
mov rbx, rcx ; rbx = FRotator*
mov rsi, rdx ; rsi = FQuat*
; sinCos(&SP, &CP, Pitch * DIVIDE_BY_2)
lea rcx, [rsp + 32]
lea rdx, [rsp + 36]
movss xmm2, DWORD PTR [rbx]
mulss xmm2, DWORD PTR [fDivBy2]
call Utils_sinCos
; sinCos(&SY, &CY, Yaw * DIVIDE_BY_2)
lea rcx, [rsp + 40]
lea rdx, [rsp + 44]
movss xmm2, DWORD PTR [rbx + 4]
mulss xmm2, DWORD PTR [fDivBy2]
call Utils_sinCos
; sinCos(&SR, &CR, Roll * DIVIDE_BY_2)
lea rcx, [rsp + 48]
lea rdx, [rsp + 52]
movss xmm2, DWORD PTR [rbx + 8]
mulss xmm2, DWORD PTR [fDivBy2]
call Utils_sinCos
; Reload locals: SP=rsp+32 CP=rsp+36 SY=rsp+40 CY=rsp+44 SR=rsp+48 CR=rsp+52
; X = CR*SP*SY - SR*CP*CY
movss xmm0, DWORD PTR [rsp + 52] ; CR
mulss xmm0, DWORD PTR [rsp + 32] ; * SP
mulss xmm0, DWORD PTR [rsp + 40] ; * SY
movss xmm1, DWORD PTR [rsp + 48] ; SR
mulss xmm1, DWORD PTR [rsp + 36] ; * CP
mulss xmm1, DWORD PTR [rsp + 44] ; * CY
subss xmm0, xmm1
movss DWORD PTR [rsi], xmm0 ; out->X
; Y = -(CR*SP*CY + SR*CP*SY)
movss xmm0, DWORD PTR [rsp + 52] ; CR
mulss xmm0, DWORD PTR [rsp + 32] ; * SP
mulss xmm0, DWORD PTR [rsp + 44] ; * CY
movss xmm1, DWORD PTR [rsp + 48] ; SR
mulss xmm1, DWORD PTR [rsp + 36] ; * CP
mulss xmm1, DWORD PTR [rsp + 40] ; * SY
addss xmm0, xmm1
xorps xmm2, xmm2
subss xmm2, xmm0 ; negate
movss DWORD PTR [rsi + 4], xmm2 ; out->Y
; Z = CR*CP*SY - SR*SP*CY
movss xmm0, DWORD PTR [rsp + 52] ; CR
mulss xmm0, DWORD PTR [rsp + 36] ; * CP
mulss xmm0, DWORD PTR [rsp + 40] ; * SY
movss xmm1, DWORD PTR [rsp + 48] ; SR
mulss xmm1, DWORD PTR [rsp + 32] ; * SP
mulss xmm1, DWORD PTR [rsp + 44] ; * CY
subss xmm0, xmm1
movss DWORD PTR [rsi + 8], xmm0 ; out->Z
; W = CR*CP*CY + SR*SP*SY
movss xmm0, DWORD PTR [rsp + 52] ; CR
mulss xmm0, DWORD PTR [rsp + 36] ; * CP
mulss xmm0, DWORD PTR [rsp + 44] ; * CY
movss xmm1, DWORD PTR [rsp + 48] ; SR
mulss xmm1, DWORD PTR [rsp + 32] ; * SP
mulss xmm1, DWORD PTR [rsp + 40] ; * SY
addss xmm0, xmm1
movss DWORD PTR [rsi + 12], xmm0 ; out->W
add rsp, 64
pop rsi
pop rbx
pop rbp
ret
Utils_RotToQuat ENDP
; Utils_VecToRot(RCX=FVector* vec, RDX=FRotator* out_rot)
; FVector: X[+0] Y[+4] Z[+8]
; FRotator: Pitch[+0] Yaw[+4] Roll[+8]
; Yaw = atan2f(Y, X) * (180/PI)
; Pitch = atan2f(Z, sqrtf(X*X + Y*Y)) * (180/PI)
; Roll = 0
; Frame: push rbp rbx rsi + sub 48 (3 pushes -> 0; sub 48 -> 0)
Utils_VecToRot PROC
push rbp
push rbx
push rsi
sub rsp, 48
mov rbx, rcx ; rbx = FVector*
mov rsi, rdx ; rsi = FRotator*
; Yaw = atan2f(Y, X) * (180/PI)
movss xmm0, DWORD PTR [rbx + 4] ; xmm0 = Y (y argument to atan2)
movss xmm1, DWORD PTR [rbx] ; xmm1 = X (x argument to atan2)
call atan2f ; XMM0 = atan2(Y, X) in radians
mulss xmm0, DWORD PTR [fRadToDeg]
movss DWORD PTR [rsi + 4], xmm0 ; out->Yaw
; Pitch = atan2f(Z, sqrtf(X*X + Y*Y)) * (180/PI)
movss xmm0, DWORD PTR [rbx] ; X
mulss xmm0, xmm0 ; X*X
movss xmm1, DWORD PTR [rbx + 4] ; Y
mulss xmm1, xmm1 ; Y*Y
addss xmm0, xmm1 ; X*X + Y*Y
sqrtss xmm0, xmm0 ; sqrtf(X*X + Y*Y) [hardware SSE]
movss xmm1, xmm0 ; xmm1 = sqrt (x arg)
movss xmm0, DWORD PTR [rbx + 8] ; xmm0 = Z (y arg)
call atan2f ; XMM0 = atan2(Z, sqrt)
mulss xmm0, DWORD PTR [fRadToDeg]
movss DWORD PTR [rsi], xmm0 ; out->Pitch
; Roll = 0.0
xorps xmm0, xmm0
movss DWORD PTR [rsi + 8], xmm0 ; out->Roll
add rsp, 48
pop rsi
pop rbx
pop rbp
ret
Utils_VecToRot ENDP
; Utils_FRand() -> XMM0 (float in [0.0, 1.0))
; Returns (float)rand() / 32767.0f
; Frame: push rbp + sub 32 (1 push -> 0; sub 32 -> 0)
Utils_FRand PROC
push rbp
sub rsp, 32
call rand ; EAX = random int [0, RAND_MAX]
cvtsi2ss xmm0, eax ; convert to float
divss xmm0, DWORD PTR [fRandMax] ; / 32767.0
add rsp, 32
pop rbp
ret
Utils_FRand ENDP
; Utils_RandomIntInRange(ECX=min, EDX=max) -> EAX
; Returns a random integer in [min, max] inclusive.
; Uses: rand() % (max - min + 1) + min
; Frame: push rbp rbx rsi + sub 32 (3 pushes -> 0; sub 32 -> 0)
Utils_RandomIntInRange PROC
push rbp
push rbx
push rsi
sub rsp, 32
mov ebx, ecx ; ebx = min
sub edx, ecx ; edx = max - min
inc edx ; edx = range = max - min + 1
mov esi, edx ; esi = range
call rand ; EAX = rand value
; EAX % range: use unsigned div (xdiv) since values are non-negative
xor edx, edx
test esi, esi
jle @@zero_range ; guard: range <= 0 -> return min
div esi ; EDX = EAX % range
add edx, ebx ; result = remainder + min
mov eax, edx
jmp @@done
@@zero_range:
mov eax, ebx ; return min
@@done:
add rsp, 32
pop rsi
pop rbx
pop rbp
ret
Utils_RandomIntInRange ENDP
END