diff --git a/hooks.asm b/hooks.asm index 310db64..048112a 100644 --- a/hooks.asm +++ b/hooks.asm @@ -2,7 +2,7 @@ INCLUDE include\master.inc ; Local offset supplements APLAYER_CurrentNetSpeed EQU 038h -UNETCONN_InternalAck EQU 0C8h ; bool — estimated, verify against dump +UNETCONN_InternalAck EQU 0C8h ; bool - estimated, verify against dump AFPCA_OverriddenBackpackSize EQU 0720h .data? @@ -44,18 +44,18 @@ Hooks_KickPlayer PROC ret Hooks_KickPlayer ENDP -; Hooks_LocalPlayerSpawnPlayActor — tail-call or early return +; Hooks_LocalPlayerSpawnPlayActor - tail-call or early return ; void* Hooks_LocalPlayerSpawnPlayActor(ULocalPlayer*, FString*, FString*, UWorld*) ; If not yet traveled: tail-call original (all regs untouched). ; If already traveled: return 1 (suppress duplicate actor spawns). ; -; Stack: no frame — either ret-1 or jmp to original (tail call). +; Stack: no frame - either ret-1 or jmp to original (tail call). Hooks_LocalPlayerSpawnPlayActor PROC movzx eax, BYTE PTR [bTraveled] test al, al jnz @@already_traveled - ; Not traveled yet — forward all args unchanged via tail call + ; Not traveled yet - forward all args unchanged via tail call jmp QWORD PTR [Native_LocalPlayer_SpawnPlayActor] @@already_traveled: @@ -127,7 +127,7 @@ Hooks_World_NotifyControlMessage PROC Hooks_World_NotifyControlMessage ENDP ; Hooks_Beacon_NotifyAcceptingConnection(AOnlineBeacon*) -> uint8 -; Beacon is accepting a new peer — forward to World's handler. +; Beacon is accepting a new peer - forward to World's handler. ; Args: RCX = AOnlineBeacon* ; ; Stack: push rbp + push rbx = 2 pushes; sub 40 -> 0 @@ -155,7 +155,7 @@ Hooks_Beacon_NotifyAcceptingConnection PROC Hooks_Beacon_NotifyAcceptingConnection ENDP ; Hooks_SeamlessTravelHandlerForWorld(UEngine*, UWorld*) -; Args: RCX=Engine, RDX=UWorld* (ignored — use GetWorld()) +; Args: RCX=Engine, RDX=UWorld* (ignored - use GetWorld()) ; ; Stack: push rbp + push rbx = 2 pushes; sub 40 -> 0 Hooks_SeamlessTravelHandlerForWorld PROC @@ -221,7 +221,7 @@ Hooks_PostRender ENDP ; Args: RCX=NetDriver, XMM1=DeltaSeconds (float) ; ; Stack: push rbp,rbx = 2 pushes (RSP=8 after 2×8); sub 40(=8) -> 0 -; [rsp+32] — spill slot for DeltaSeconds (REAL4) +; [rsp+32] - spill slot for DeltaSeconds (REAL4) Hooks_TickFlush PROC push rbp push rbx @@ -241,7 +241,7 @@ Hooks_TickFlush PROC test eax, eax jle @@call_orig - ; ClientConnections[0] — dereference Data ptr + ; ClientConnections[0] - dereference Data ptr mov rax, QWORD PTR [rbx + UNETDRIVER_ClientConns] mov rax, QWORD PTR [rax] ; first element test rax, rax @@ -316,7 +316,7 @@ Hooks_GetPlayerViewPoint PROC ret Hooks_GetPlayerViewPoint ENDP -; Hooks_SpawnPlayActor — spawn and set up new player controller +; Hooks_SpawnPlayActor - spawn and set up new player controller ; Original: APlayerController* (*)(UWorld*, UPlayer*, ENetRole, FURL&, void*, FString&, uint8) ; We replace UWorld* with GetWorld() and wire up the resulting PC. ; @@ -325,7 +325,7 @@ Hooks_GetPlayerViewPoint ENDP ; RDX = UPlayer* NewPlayer ; R8 = ENetRole ; R9 = FURL* -; [RSP+40] = void* UniqueId (5th arg — see note) +; [RSP+40] = void* UniqueId (5th arg - see note) ; [RSP+48] = FString* Error (6th arg) ; [RSP+56] = uint8 NetIdx (7th arg) ; @@ -413,10 +413,10 @@ Hooks_SpawnPlayActor ENDP ; uint8 MessageType, FBitReader* Bunch ; ; Dispatch by MessageType: -; 4 (NMT_Netspeed) — set Connection->CurrentNetSpeed = 30000 -; 5 (NMT_Login) — receive FStrings + UniqueId, then WelcomePlayer -; 15 (NMT_PCSwap) — call original Beacon_NotifyControlMessage -; default — call Native_World_NotifyControlMessage(GetWorld(), ...) +; 4 (NMT_Netspeed) - set Connection->CurrentNetSpeed = 30000 +; 5 (NMT_Login) - receive FStrings + UniqueId, then WelcomePlayer +; 15 (NMT_PCSwap) - call original Beacon_NotifyControlMessage +; default - call Native_World_NotifyControlMessage(GetWorld(), ...) ; ; Stack: push rbp,rbx,rsi = 3 pushes (RSP=0 after 3×8). ; sub 112(=0) -> 0 . @@ -470,7 +470,7 @@ Hooks_Beacon_NotifyControlMessage PROC ; FString str2 at [rsp+48..63] mov QWORD PTR [rsp + 48], rax mov QWORD PTR [rsp + 56], rax - ; FUniqueNetIdRepl at [rsp+64..103] — 40 bytes (5 QWORDs) + ; FUniqueNetIdRepl at [rsp+64..103] - 40 bytes (5 QWORDs) mov QWORD PTR [rsp + 64], rax mov QWORD PTR [rsp + 72], rax mov QWORD PTR [rsp + 80], rax @@ -561,7 +561,7 @@ Hooks_ProcessEventHook PROC cmp rsi, r12 ; Function == ReadyToStartMatch? jne @@check_traveled - ; Match — server is ready to start: set flag, travel, hook network + ; Match - server is ready to start: set flag, travel, hook network mov BYTE PTR [bPlayButton], 1 call SDK_GetWorld diff --git a/include/enums.inc b/include/enums.inc index ba60f88..18af1ea 100644 --- a/include/enums.inc +++ b/include/enums.inc @@ -98,7 +98,7 @@ DLL_THREAD_DETACH EQU 3 ; Vtable slot indices VTABLE_SLOT_ProcessEvent EQU 40h ; = 64 decimal -VTABLE_SLOT_ServerReplicateActors EQU 53h ; = 83 decimal — on UReplicationDriver +VTABLE_SLOT_ServerReplicateActors EQU 53h ; = 83 decimal - on UReplicationDriver ; Network message type codes (NMT_ values) NMT_Netspeed EQU 4 diff --git a/include/externs.inc b/include/externs.inc index abd2857..89c46dc 100644 --- a/include/externs.inc +++ b/include/externs.inc @@ -1,4 +1,4 @@ -; sdk_globals.asm — Global UE4 runtime pointers and GObjects +; sdk_globals.asm - Global UE4 runtime pointers and GObjects EXTERNDEF Imagebase :QWORD ; base address of FortniteClient-Win64-Shipping.exe EXTERNDEF ProcessEvent :QWORD ; void (*)(UObject*, UFunction*, void*) EXTERNDEF FMemory_Malloc :QWORD ; void* (*)(int32 size, int32 alignment) @@ -7,18 +7,18 @@ EXTERNDEF FMemory_Free :QWORD ; void (*)(void* original) EXTERNDEF FNameToString :QWORD ; void (*)(FName* name, FString& out) EXTERNDEF GObjects :QWORD ; TUObjectArray* (the global UObject array) -; sdk_globals.asm — Global mutable state -EXTERNDEF bTraveled :BYTE ; bool — has the server traveled yet? -EXTERNDEF bPlayButton :BYTE ; bool — play button clicked? -EXTERNDEF bListening :BYTE ; bool — server is listening? -EXTERNDEF bStartedBus :BYTE ; bool — battle bus launched? -EXTERNDEF bSpawnedFloorLoot :BYTE ; bool — floor loot spawned? +; sdk_globals.asm - Global mutable state +EXTERNDEF bTraveled :BYTE ; bool - has the server traveled yet? +EXTERNDEF bPlayButton :BYTE ; bool - play button clicked? +EXTERNDEF bListening :BYTE ; bool - server is listening? +EXTERNDEF bStartedBus :BYTE ; bool - battle bus launched? +EXTERNDEF bSpawnedFloorLoot :BYTE ; bool - floor loot spawned? EXTERNDEF HostBeacon :QWORD ; AFortOnlineBeaconHost* EXTERNDEF ExistingBuildings :QWORD ; pointer to TArray head (Data ptr) EXTERNDEF ExistingBuildingsNum :DWORD ; current Num of ExistingBuildings array EXTERNDEF ExistingBuildingsMax :DWORD ; current Max of ExistingBuildings array -; native.asm — Native:: function-pointer table +; native.asm - Native:: function-pointer table ; Native::Actor EXTERNDEF Native_Actor_GetNetMode :QWORD ; Native::PlayerController @@ -58,10 +58,10 @@ EXTERNDEF Native_Engine_SeamlessTravelHandlerForWorld :QWORD ; Native::GameViewportClient EXTERNDEF Native_GameViewportClient_PostRender :QWORD -; native.asm — InitializeAll entry point +; native.asm - InitializeAll entry point EXTERNDEF Native_InitializeAll :PROC -; patterns.asm — Pattern byte arrays and mask strings (.const) +; patterns.asm - Pattern byte arrays and mask strings (.const) EXTERNDEF Pat_FNameToString :BYTE EXTERNDEF Pat_GObjects :BYTE EXTERNDEF Pat_Free :BYTE @@ -90,7 +90,7 @@ EXTERNDEF Pat_PostRender :BYTE EXTERNDEF Pat_CollectGarbage :BYTE EXTERNDEF Pat_NetDebug :BYTE -; util.asm — Utility functions +; util.asm - Utility functions EXTERNDEF Utils_FindPattern :PROC ; (RCX=pszPattern, EDX=bRelative, R8D=offset) -> RAX:QWORD (address or 0) EXTERNDEF Utils_Sin :PROC ; (angle:REAL4) -> REAL4 (result in XMM0) EXTERNDEF Utils_Cos :PROC ; (angle:REAL4) -> REAL4 (result in XMM0) @@ -98,14 +98,14 @@ EXTERNDEF Utils_RotToQuat :PROC ; (rot:PTR FRotator, out_quat:PTR FQ EXTERNDEF Utils_VecToRot :PROC ; (vec:PTR FVector, out_rot:PTR FRotator) EXTERNDEF Utils_RandomIntInRange :PROC ; (min:DWORD, max:DWORD) -> DWORD -; logger.asm — switched to sprintf +; logger.asm - switched to sprintf EXTERNDEF Logger_Initialize :PROC ; () EXTERNDEF Logger_LogInfo :PROC ; (RCX=msg:PTR BYTE) EXTERNDEF Logger_LogWarn :PROC ; (RCX=msg:PTR BYTE) EXTERNDEF Logger_LogError :PROC ; (RCX=msg:PTR BYTE) -EXTERNDEF Logger_LogInfoFmt :PROC ; (RCX=fmt:PTR BYTE, RDX/R8/R9=args) — printf passthrough +EXTERNDEF Logger_LogInfoFmt :PROC ; (RCX=fmt:PTR BYTE, RDX/R8/R9=args) - printf passthrough -; hooks.asm — All hook implementations +; hooks.asm - All hook implementations EXTERNDEF Hooks_TickFlush :PROC EXTERNDEF Hooks_ProcessEventHook :PROC EXTERNDEF Hooks_LocalPlayerSpawnPlayActor :PROC @@ -123,20 +123,20 @@ EXTERNDEF Hooks_Beacon_NotifyControlMessage :PROC EXTERNDEF Hooks_PostRender :PROC EXTERNDEF Hooks_InitNetworkHooks :PROC -; ufunctionhooks.asm — UFunction dispatch hooks +; ufunctionhooks.asm - UFunction dispatch hooks EXTERNDEF UFunctionHooks_Initialize :PROC EXTERNDEF UFunctionHooks_ToHook_Data :QWORD ; base of UFunction* array EXTERNDEF UFunctionHooks_ToHook_Num :DWORD ; count of registered hooks EXTERNDEF UFunctionHooks_ToCall_Data :QWORD ; base of handler QWORD* array (BYTE (*)(UObject*,void*)) -; sdk/sdk_classes.asm — UObject / GObjects accessors +; sdk/sdk_classes.asm - UObject / GObjects accessors EXTERNDEF SDK_GetObjectByIndex :PROC ; (index:DWORD) -> QWORD (UObject*) EXTERNDEF SDK_FindObject :PROC ; (name:PTR BYTE) -> QWORD (UObject*) EXTERNDEF SDK_FindClass :PROC ; (name:PTR BYTE) -> QWORD (UClass*) EXTERNDEF SDK_GetWorld :PROC ; () -> QWORD (UWorld*) EXTERNDEF SDK_GetEngine :PROC ; () -> QWORD (UFortEngine*) -; sdk/sdk_classes.asm — TArray helpers (element-size-agnostic) +; sdk/sdk_classes.asm - TArray helpers (element-size-agnostic) EXTERNDEF TArray_Num :PROC ; (arr:PTR TArrayHeader) -> DWORD EXTERNDEF TArray_GetByIndex :PROC ; (arr:PTR TArrayHeader, idx:DWORD, elem_size:DWORD) -> QWORD EXTERNDEF TArray_Add :PROC ; (arr:PTR TArrayHeader, elem:PTR BYTE, elem_size:DWORD) -> DWORD @@ -144,15 +144,15 @@ EXTERNDEF TArray_RemoveAt :PROC ; (arr:PTR TArrayHeader, idx:DWORD, EXTERNDEF TArray_RemoveSingle :PROC ; (arr:PTR TArrayHeader, elem:PTR BYTE, elem_size:DWORD) -> BYTE EXTERNDEF TArray_FreeArray :PROC ; (arr:PTR TArrayHeader) -; sdk/sdk_classes.asm — FString helpers +; sdk/sdk_classes.asm - FString helpers EXTERNDEF FString_FromWideChar :PROC ; (out:PTR TArrayHeader, wstr:PTR WORD) EXTERNDEF FString_Free :PROC ; (str:PTR TArrayHeader) -; sdk/sdk_fort.asm — Fortnite-specific helpers +; sdk/sdk_fort.asm - Fortnite-specific helpers EXTERNDEF FortHelper_GetAbilitySystemComponent :PROC ; (pawn:QWORD) -> QWORD EXTERNDEF FortHelper_GetWorldInventory :PROC ; (pc:QWORD) -> QWORD EXTERNDEF FortHelper_GetQuickBars :PROC ; (pc:QWORD) -> QWORD -EXTERNDEF SDK_CreateConsole :PROC ; () — spawns UConsole on GameViewport +EXTERNDEF SDK_CreateConsole :PROC ; () - spawns UConsole on GameViewport ; logic/game.asm EXTERNDEF Game_Start :PROC diff --git a/native.asm b/native.asm index ab8812a..0ed776c 100644 --- a/native.asm +++ b/native.asm @@ -127,7 +127,7 @@ Native_InitializeAll PROC xor r8d, r8d call Utils_FindPattern mov QWORD PTR [Native_OnlineBeacon_PauseBeaconRequests], rax - ; Also store in the host-specific slot — same function pointer + ; Also store in the host-specific slot - same function pointer mov QWORD PTR [Native_OnlineBeaconHost_PauseBeaconRequests], rax ; OnlineBeacon::NotifyAcceptingConnection (direct) diff --git a/raider.asm b/raider.asm index def4219..f10bbbb 100644 --- a/raider.asm +++ b/raider.asm @@ -15,7 +15,7 @@ szBaseAddrFmt DB "[INFO] Base Address: 0x%I64X", 0Ah, 0 .code -; Main — worker thread entry point +; Main - worker thread entry point ; RCX = lpParam (hModule passed from DllMain; not used inside Main) ; ; Stack frame layout (after prologue): @@ -105,7 +105,7 @@ Main PROC ret Main ENDP -; DllMain — DLL entry point +; DllMain - DLL entry point ; RCX = hModule (HMODULE) ; EDX = dwReason (DWORD) ; R8 = lpReserved (LPVOID) diff --git a/sdk/sdk_classes.asm b/sdk/sdk_classes.asm index 9a2e014..6d93001 100644 --- a/sdk/sdk_classes.asm +++ b/sdk/sdk_classes.asm @@ -766,7 +766,7 @@ SDK_FindClass PROC test eax, eax jnz @@scan_loop - ; Exact match — reload object ptr + ; Exact match - reload object ptr mov rax, QWORD PTR [rbx] lea rcx, [rsi - 1] imul rcx, rcx, 24 @@ -798,7 +798,7 @@ ProcessEvent_Call PROC mov rax, QWORD PTR [ProcessEvent] test rax, rax jz @@done - call rax ; ProcessEvent(obj, fn, params) — rcx/rdx/r8 already set + call rax ; ProcessEvent(obj, fn, params) - rcx/rdx/r8 already set @@done: add rsp, 40 diff --git a/sdk_globals.asm b/sdk_globals.asm index cba86d5..17c91e6 100644 --- a/sdk_globals.asm +++ b/sdk_globals.asm @@ -4,13 +4,13 @@ INCLUDE include\master.inc ; Core UE4 runtime function pointers ProcessEvent QWORD ? ; void (*)(UObject*, UFunction*, void*) -Imagebase QWORD ? ; uintptr_t — base of game .exe module +Imagebase QWORD ? ; uintptr_t - base of game .exe module FMemory_Malloc QWORD ? ; void* (*)(int32 size, int32 alignment) FMemory_Realloc QWORD ? ; void* (*)(void* mem, int64 newSize, uint32 align) FMemory_Free QWORD ? ; void (*)(void* mem) FNameToString QWORD ? ; void (*)(FName* this, FString& out) -GObjects QWORD ? ; TUObjectArray* — global UObject table +GObjects QWORD ? ; TUObjectArray* - global UObject table ; Native:: function pointers ; Actor @@ -70,17 +70,17 @@ Native_Engine_SeamlessTravelHandlerForWorld QWORD ? Native_GameViewportClient_PostRender QWORD ? ; Game state globals -bTraveled BYTE ? ; bool — server has traveled to map -bPlayButton BYTE ? ; bool — play button clicked -bListening BYTE ? ; bool — server is listening for connections -bStartedBus BYTE ? ; bool — battle bus has launched -bSpawnedFloorLoot BYTE ? ; bool — floor loot has been spawned +bTraveled BYTE ? ; bool - server has traveled to map +bPlayButton BYTE ? ; bool - play button clicked +bListening BYTE ? ; bool - server is listening for connections +bStartedBus BYTE ? ; bool - battle bus has launched +bSpawnedFloorLoot BYTE ? ; bool - floor loot has been spawned BYTE 3 DUP (?) ; pad to QWORD boundary ; Pointer globals HostBeacon QWORD ? ; AFortOnlineBeaconHost* -; ExistingBuildings — std::vector represented as TArrayHeader +; ExistingBuildings - std::vector represented as TArrayHeader ; Layout matches TArrayHeader: Data(QWORD), Count(DWORD), Max(DWORD) ExistingBuildings QWORD ? ; Data: ABuildingActor** array ptr ExistingBuildingsNum DWORD ? ; Count diff --git a/ufunctionhooks.asm b/ufunctionhooks.asm new file mode 100644 index 0000000..eac8c7b --- /dev/null +++ b/ufunctionhooks.asm @@ -0,0 +1,1201 @@ +INCLUDE include\master.inc + +ACONTROLLER_Pawn EQU 0260h ; AController::Pawn (APawn*) +ACONTROLLER_PlayerState EQU 0248h ; AController::PlayerState (APlayerState*) +APAWN_Controller EQU 0228h ; APawn::Controller (AController*) +APAWN_PlayerState EQU 0258h ; APawn::PlayerState +AFPCA_bIsDisconnecting EQU 1674h ; AFortPlayerControllerAthena::bIsDisconnecting (bool) +AOBH_ListenPort EQU 0208h ; AOnlineBeaconHost::ListenPort (int32) +AOBH_NetDriver EQU 0220h ; AOnlineBeaconHost::Driver (UNetDriver*) +ABSMA_EditingPlayer EQU 0480h ; ABuildingSMActor::EditingPlayer +UWORLD_NetDriver EQU 0E8h ; UWorld::NetDriver (UNetDriver*) +UWORLD_AuthorityGameMode EQU 0118h ; UWorld::AuthorityGameMode (AGameMode*) +FURL_Port EQU 020h ; FURL::Port (DWORD, per structs.inc) +VTABLE_ServerReplicateActors EQU (083h * 8) ; vtable byte offset for slot 0x53 + +MAX_PEHOOKS EQU 32 ; capacity of the hook arrays + +.data? + +; Public pointers consumed by Hooks_ProcessEventHook +UFunctionHooks_ToHook_Data QWORD ? ; -> _ToHook_Storage (set in Initialize) +UFunctionHooks_ToHook_Num DWORD ? + DWORD ? ; pad to QWORD +UFunctionHooks_ToCall_Data QWORD ? ; -> _ToCall_Storage (set in Initialize) + +; Internal flat arrays (MAX_PEHOOKS elements each) +_ToHook_Storage QWORD MAX_PEHOOKS DUP (?) +_ToCall_Storage QWORD MAX_PEHOOKS DUP (?) + +; Lazily-resolved UFunction* cache (used by handlers) +pFn_K2_DestroyActor QWORD ? +pFn_ClientOnPawnRevived QWORD ? +pFn_ForceNetUpdate QWORD ? +pFn_OnRep_ReplicatedAnimMontage QWORD ? + +; Cached class pointers (used by ReadyToStartMatch) +pClass_FortOnlineBeaconHost QWORD ? + +.const + +szFn_ServerTryActivateAbility DB "Function GameplayAbilities.AbilitySystemComponent.ServerTryActivateAbility", 0 +szFn_ServerTryActivateWithEventData DB "Function GameplayAbilities.AbilitySystemComponent.ServerTryActivateAbilityWithEventData", 0 +szFn_ServerAbilityRPCBatch DB "Function FortniteGame.FortAbilitySystemComponent.ServerAbilityRPCBatch", 0 +szFn_ServerHandlePickup DB "Function FortniteGame.FortPlayerPawn.ServerHandlePickup", 0 +szFn_CheatScript DB "Function FortniteGame.FortGameModeAthena.CheatScript", 0 +szFn_OnDeathServer DB "Function FortniteGame.BuildingSMActor.OnDeathServer", 0 +szFn_ServerCreateBuildingActor DB "Function FortniteGame.BuildingSMActor.ServerCreateBuildingActor", 0 +szFn_ServerBeginEditingBuilding DB "Function FortniteGame.BuildingSMActor.ServerBeginEditingBuildingActor", 0 +szFn_ServerSpawnDeco DB "Function FortniteGame.FortDecoTool.ServerSpawnDeco", 0 +szFn_ServerEditBuildingActor DB "Function FortniteGame.BuildingSMActor.ServerEditBuildingActor", 0 +szFn_ClientOnPawnDied DB "Function FortniteGame.FortPlayerController.ClientOnPawnDied", 0 +szFn_ServerEndEditingBuilding DB "Function FortniteGame.BuildingSMActor.ServerEndEditingBuildingActor", 0 +szFn_ServerRepairBuildingActor DB "Function FortniteGame.BuildingSMActor.ServerRepairBuildingActor", 0 +szFn_ServerAttemptAircraftJump DB "Function FortniteGame.FortPlayerPawn.ServerAttemptAircraftJump", 0 +szFn_ServerReviveFromDBNO DB "Function FortniteGame.FortPlayerPawn.ServerReviveFromDBNO", 0 +szFn_ServerAttemptInteract DB "Function FortniteGame.FortPlayerPawn.ServerAttemptInteract", 0 +szFn_ServerPlayEmoteItem DB "Function FortniteGame.FortPlayerPawn.ServerPlayEmoteItem", 0 +szFn_ServerAttemptInventoryDrop DB "Function FortniteGame.FortPlayerPawn.ServerAttemptInventoryDrop", 0 +szFn_OnSpawnOutAnimEnded DB "Function FortniteGame.AthenaVictoryDrone.OnSpawnOutAnimEnded", 0 +szFn_ServerExecuteInventoryItem DB "Function FortniteGame.FortPlayerController.ServerExecuteInventoryItem", 0 +szFn_ServerReturnToMainMenu DB "Function FortniteGame.FortPlayerController.ServerReturnToMainMenu", 0 +szFn_ServerLoadingScreenDropped DB "Function FortniteGame.FortPlayerController.ServerLoadingScreenDropped", 0 +szFn_ServerChoosePart DB "Function FortniteGame.FortPlayerControllerCommon.ServerChoosePart", 0 +szFn_ReadyToStartMatch DB "Function FortniteGame.FortGameModeAthena.ReadyToStartMatch", 0 +szFn_OnAircraftExitedDropZone DB "Function FortniteGame.FortAthenaAircraft.OnAircraftExitedDropZone", 0 +szFn_ServerCheatAll DB "Function FortniteGame.FortGameModeAthena.ServerCheatAll", 0 +szFn_Logout DB "Function FortniteGame.FortGameModeAthena.Logout", 0 + +szFn_K2_DestroyActor DB "Function Engine.Actor.K2_DestroyActor", 0 +szFn_ClientOnPawnRevived DB "Function FortniteGame.FortPlayerPawn.ClientOnPawnRevived", 0 +szFn_ForceNetUpdate DB "Function Engine.Actor.ForceNetUpdate", 0 +szFn_OnRepRepAnimMontage DB "Function GameplayAbilities.AbilitySystemComponent.OnRep_ReplicatedAnimMontageForMesh", 0 + +szClass_FortOnlineBeaconHost DB "Class FortniteGame.FortOnlineBeaconHost", 0 + +szHookCount DB "[UFHOOKS] Registered %d UFunction hooks", 0Ah, 0 + +.code + + +PEHOOK_CheatScript PROC + xor al, al ; return 0 + ret +PEHOOK_CheatScript ENDP + +PEHOOK_ServerCheatAll PROC + mov al, 1 ; return 1 + ret +PEHOOK_ServerCheatAll ENDP + +PEHOOK_ServerChoosePart PROC + mov al, 1 ; return 1 + ret +PEHOOK_ServerChoosePart ENDP + +; PEHOOK_Logout - set bIsDisconnecting on the leaving controller +; RCX = AFortGameModeAthena* (GameMode) +; RDX = Params: { AController* Exiting; } ([RDX+0]) +PEHOOK_Logout PROC + mov rax, QWORD PTR [rdx] ; Exiting controller + test rax, rax + jz @@done + mov BYTE PTR [rax + AFPCA_bIsDisconnecting], 1 +@@done: + xor al, al + ret +PEHOOK_Logout ENDP + +; PEHOOK_OnSpawnOutAnimEnded - destroy the VictoryDrone actor +; RCX = AthenaVictoryDrone* (the drone that finished its exit anim) +; RDX = void* Params (empty) +; +; Calls K2_DestroyActor on the drone via ProcessEvent (lazy lookup). +; +; Stack: push rbp + push rbx = 2 pushes; sub 40 -> 0 +PEHOOK_OnSpawnOutAnimEnded PROC + push rbp + push rbx + sub rsp, 40 ; 2 pushes: RSP=8; sub40 -> 0 + + mov rbx, rcx ; save drone actor* + + ; Lazy-resolve K2_DestroyActor UFunction + mov rax, QWORD PTR [pFn_K2_DestroyActor] + test rax, rax + jnz @@have_fn + lea rcx, szFn_K2_DestroyActor + call SDK_FindObject + mov QWORD PTR [pFn_K2_DestroyActor], rax +@@have_fn: + test rax, rax + jz @@done + + ; ProcessEvent(Drone, K2_DestroyActor, NULL) + mov rcx, rbx + mov rdx, rax + xor r8d, r8d + call QWORD PTR [ProcessEvent] + +@@done: + add rsp, 40 + pop rbx + pop rbp + xor al, al + ret +PEHOOK_OnSpawnOutAnimEnded ENDP + +; PEHOOK_ServerLoadingScreenDropped - apply abilities to pawn +; RCX = AFortPlayerController* +; Gets Pawn from PC, calls Abilities_ApplyAbilities(Pawn). +; +; Stack: push rbp + push rbx = 2 pushes; sub 40 -> 0 +PEHOOK_ServerLoadingScreenDropped PROC + push rbp + push rbx + sub rsp, 40 + + mov rbx, rcx ; PC + ; Pawn = PC->Pawn (AController::Pawn at +ACONTROLLER_Pawn) + mov rax, QWORD PTR [rbx + ACONTROLLER_Pawn] + test rax, rax + jz @@done + + mov rcx, rax + call Abilities_ApplyAbilities + +@@done: + add rsp, 40 + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ServerLoadingScreenDropped ENDP + +; PEHOOK_ServerAttemptInventoryDrop +; RCX = AFortPlayerPawn* (the pawn dropping) +; RDX = Params: { FGuid ItemGuid; int Count; } +; Gets PC from Pawn, calls Inventory_OnDrop(PC, Params). +; +; Stack: push rbp + push rbx + push rsi = 3 pushes (RSP=0); sub 32 -> 0 +PEHOOK_ServerAttemptInventoryDrop PROC + push rbp + push rbx + push rsi + sub rsp, 32 ; 3 pushes: RSP=0; sub32 -> 0 + + mov rbx, rcx ; Pawn + mov rsi, rdx ; Params + + ; Controller = Pawn->Controller + mov rax, QWORD PTR [rbx + APAWN_Controller] + test rax, rax + jz @@done + + mov rcx, rax + mov rdx, rsi + call Inventory_Update ; TODO: replace with Inventory_OnDrop(PC, Params) + +@@done: + add rsp, 32 + pop rsi + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ServerAttemptInventoryDrop ENDP + +; PEHOOK_ServerHandlePickup +; RCX = AFortPlayerPawn* (the picking-up pawn) +; RDX = Params: { AFortPickup* Pickup; float FlyTime; FVector StartDir; bool bPlaySound; } +; +; Stack: push rbp + push rbx + push rsi = 3 pushes; sub 32 -> 0 +PEHOOK_ServerHandlePickup PROC + push rbp + push rbx + push rsi + sub rsp, 32 + + mov rbx, rcx ; Pawn + mov rsi, rdx ; Params + + mov rax, QWORD PTR [rbx + APAWN_Controller] + test rax, rax + jz @@done + + mov rcx, rax ; PC + mov rdx, rsi ; Params + call Inventory_Update ; TODO: Inventory_OnPickup(PC, Params) + +@@done: + add rsp, 32 + pop rsi + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ServerHandlePickup ENDP + +; PEHOOK_ServerExecuteInventoryItem +; RCX = AFortPlayerController* +; RDX = Params: { FGuid ItemGuid; } ([RDX+0]) +; +; Stack: push rbp + push rbx = 2 pushes; sub 40 -> 0 +PEHOOK_ServerExecuteInventoryItem PROC + push rbp + push rbx + sub rsp, 40 + + mov rbx, rcx ; PC + ; Guid at [RDX+0] - pass rdx as-is (Inventory_EquipInventoryItem takes (PC, Guid*)) + mov rcx, rbx + ; rdx = Params (Guid is at offset 0, so Params == &Guid) + call Inventory_EquipWeaponDefinition ; TODO: Inventory_EquipInventoryItem(PC, Guid*) + + add rsp, 40 + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ServerExecuteInventoryItem ENDP + +; PEHOOK_ServerReturnToMainMenu +; RCX = AFortPlayerController* - set bIsDisconnecting + return true +PEHOOK_ServerReturnToMainMenu PROC + test rcx, rcx + jz @@done + mov BYTE PTR [rcx + AFPCA_bIsDisconnecting], 1 +@@done: + mov al, 1 + ret +PEHOOK_ServerReturnToMainMenu ENDP + +; PEHOOK_OnDeathServer - remove dying building from ExistingBuildings +; RCX = ABuildingSMActor* (dying actor) +; Scans ExistingBuildings array; on match, swaps with last and decrements Num. +; +; Stack: push rbp + push rbx + push rsi + push rdi = 4 pushes (RSP=8); sub 40 -> 0 +PEHOOK_OnDeathServer PROC + push rbp + push rbx + push rsi + push rdi + sub rsp, 40 ; 4 pushes: RSP=8; sub40 -> 0 + + mov rbx, rcx ; dying actor + + ; Load ExistingBuildings array + mov rsi, QWORD PTR [ExistingBuildings] ; Data ptr (QWORD**) + mov edi, DWORD PTR [ExistingBuildingsNum]; Num + + test rsi, rsi + jz @@done + test edi, edi + jle @@done + + xor ebp, ebp ; i = 0 +@@scan_loop: + cmp ebp, edi + jge @@done + + mov rax, QWORD PTR [rsi + rbp * 8] ; ExistingBuildings[i] + cmp rax, rbx + jne @@next + + ; Found at index i - swap with last element (unordered remove) + dec edi ; new Num = Num-1 + cmp ebp, edi ; if i == new Num, no swap needed + je @@erase + mov rax, QWORD PTR [rsi + rdi * 8] ; last element + mov QWORD PTR [rsi + rbp * 8], rax ; [i] = last +@@erase: + mov DWORD PTR [ExistingBuildingsNum], edi + jmp @@done + +@@next: + inc ebp + jmp @@scan_loop + +@@done: + add rsp, 40 + pop rdi + pop rsi + pop rbx + pop rbp + xor al, al + ret +PEHOOK_OnDeathServer ENDP + +; PEHOOK_ServerEndEditingBuildingActor +; RCX = ABuildingSMActor* (building being released) +; Clears EditingPlayer field on the building. +; +; TODO: also clear EditTool->EditActor if we have AFortDecoTool offset. +PEHOOK_ServerEndEditingBuildingActor PROC + test rcx, rcx + jz @@done + mov QWORD PTR [rcx + ABSMA_EditingPlayer], 0 +@@done: + xor al, al + ret +PEHOOK_ServerEndEditingBuildingActor ENDP + +; PEHOOK_ServerRepairBuildingActor +; RCX = AFortPlayerPawn* (pawn issuing repair) +; RDX = Params: { ABuildingSMActor* BuildingActorToRepair; } +; +; Calls RepairBuilding (amount 50) via ProcessEvent on the target building. +; Stack: push rbp + push rbx + push rsi = 3 pushes; sub 32 -> 0 +PEHOOK_ServerRepairBuildingActor PROC + push rbp + push rbx + push rsi + sub rsp, 32 + + mov rbx, rcx ; Pawn (unused for repair call itself) + mov rsi, QWORD PTR [rdx] ; Params->BuildingActorToRepair (QWORD at [RDX+0]) + test rsi, rsi + jz @@done + + ; TODO : call RepairBuilding via ProcessEvent(Building, RepairFn, &AmountParams) + ; For now: ForceNetUpdate to signal changes + mov rcx, rsi + call QWORD PTR [ProcessEvent] ; placeholder + +@@done: + add rsp, 32 + pop rsi + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ServerRepairBuildingActor ENDP + +; PEHOOK_ServerReviveFromDBNO +; RCX = AFortPlayerPawn* (the DBNO pawn being revived) +; RDX = Params: { AController* EventInstigator; } +; +; Simplified: call ClientOnPawnRevived via ProcessEvent. +; Full bIsDBNO clear + health restore implemented via the revive UFunction. +; +; Stack: push rbp + push rbx = 2 pushes; sub 40 -> 0 +PEHOOK_ServerReviveFromDBNO PROC + push rbp + push rbx + sub rsp, 40 + + mov rbx, rcx ; Pawn + + ; Lazy-resolve ClientOnPawnRevived UFunction + mov rax, QWORD PTR [pFn_ClientOnPawnRevived] + test rax, rax + jnz @@have_revive + lea rcx, szFn_ClientOnPawnRevived + call SDK_FindObject + mov QWORD PTR [pFn_ClientOnPawnRevived], rax +@@have_revive: + test rax, rax + jz @@done + + ; ProcessEvent(Pawn, ClientOnPawnRevived, NULL) + mov rcx, rbx + mov rdx, rax + xor r8d, r8d + call QWORD PTR [ProcessEvent] + +@@done: + add rsp, 40 + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ServerReviveFromDBNO ENDP + +; PEHOOK_ServerAttemptInteract +; RCX = AFortPlayerPawn* (interacting pawn) +; RDX = Params: { AActor* ReceivingActor; } +; +; If ReceivingActor is a pawn: call PEHOOK_ServerReviveFromDBNO logic. +; Otherwise (container etc.): stub for (bAlreadySearched). +; +; Stack: push rbp + push rbx + push rsi = 3 pushes; sub 32 -> 0 +PEHOOK_ServerAttemptInteract PROC + push rbp + push rbx + push rsi + sub rsp, 32 + + mov rbx, rcx ; interacting Pawn + mov rsi, QWORD PTR [rdx] ; ReceivingActor ([Params+0]) + test rsi, rsi + jz @@done + + ; Check if ReceivingActor is a Pawn (non-null Controller means it's a player pawn) + mov rax, QWORD PTR [rsi + APAWN_Controller] + test rax, rax + jz @@container_case + + ; Pawn case - revive from DBNO + mov rcx, rsi ; the pawn to revive + xor edx, edx + call PEHOOK_ServerReviveFromDBNO + jmp @@done + +@@container_case: + ; Container case - TODO: set bAlreadySearched + OnRep + +@@done: + add rsp, 32 + pop rsi + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ServerAttemptInteract ENDP + +; PEHOOK_ServerPlayEmoteItem +; RCX = APlayerPawn_Athena_C* (the pawn emoting) +; RDX = Params: { UFortMontageItemDefinitionBase* EmoteAsset; } +; +; Sets RepAnimMontageInfo on the AbilitySystemComponent so the emote +; is replicated to all clients. +; +; Stack: push rbp + push rbx + push rsi = 3 pushes; sub 32 -> 0 +PEHOOK_ServerPlayEmoteItem PROC + push rbp + push rbx + push rsi + sub rsp, 32 + + mov rbx, rcx ; Pawn + mov rsi, QWORD PTR [rdx] ; EmoteAsset ([Params+0]) + test rsi, rsi + jz @@done + + ; Get AbilitySystemComponent from pawn + mov rcx, rbx + call FortHelper_GetAbilitySystemComponent + test rax, rax + jz @@done + + ; Write to RepAnimMontageInfo (FGameplayAbilityRepAnimMontage at ASC+0xAF0) + ; Layout: AnimMontage(+0), PlayRate(+8), Position(+C), BlendTime(+10), + ; NextSectionID(+14), bitfield(+15) + ; We store the EmoteAsset as the AnimMontage and set PlayRate=1.0 + mov QWORD PTR [rax + UASC_RepAnimMontageInfo + 000h], rsi ; AnimMontage = EmoteAsset + mov DWORD PTR [rax + UASC_RepAnimMontageInfo + 008h], 3F800000h ; PlayRate = 1.0f + mov DWORD PTR [rax + UASC_RepAnimMontageInfo + 00Ch], 0 ; Position = 0.0f + mov DWORD PTR [rax + UASC_RepAnimMontageInfo + 010h], 0 ; BlendTime = 0.0f + mov BYTE PTR [rax + UASC_RepAnimMontageInfo + 014h], 0 ; NextSectionID = 0 + mov BYTE PTR [rax + UASC_RepAnimMontageInfo + 015h], 2 ; ForcePlayBit set (bit1) + + ; ForceNetUpdate on the pawn to push replication + mov rcx, rbx + mov rdx, QWORD PTR [pFn_ForceNetUpdate] + test rdx, rdx + jz @@done + xor r8d, r8d + call QWORD PTR [ProcessEvent] + +@@done: + add rsp, 32 + pop rsi + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ServerPlayEmoteItem ENDP + +; PEHOOK_ServerSpawnDeco +; RCX = AFortDecoTool* (the deco tool actor) +; RDX = Params: { FVector WorldPosition; FRotator Rotation; ABuildingSMActor* AttachedTo; } +; WorldPos at [RDX+0], Rotation at [RDX+C], AttachedTo at [RDX+18] (after FRotator) +; +; Stack: push rbp + push rbx + push rsi = 3 pushes; sub 32 -> 0 +PEHOOK_ServerSpawnDeco PROC + push rbp + push rbx + push rsi + sub rsp, 32 + + mov rbx, rcx ; DecoTool + mov rsi, rdx ; Params + + mov rcx, rbx + mov rdx, rsi + call Spawners_SpawnDeco ; Spawners_SpawnDeco(Tool, Params) + + add rsp, 32 + pop rsi + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ServerSpawnDeco ENDP + +; PEHOOK_ServerTryActivateAbility +; RCX = UAbilitySystemComponent* (ASC) +; RDX = Params: { FGameplayAbilitySpecHandle Handle(+0,4B); bool InputPressed(+8,1B); +; FPredictionKey PredKey(+10,0x18B); } +; +; Stack: push rbp + push rbx + push rsi = 3 pushes; sub 32 -> 0 +PEHOOK_ServerTryActivateAbility PROC + push rbp + push rbx + push rsi + sub rsp, 32 + + mov rbx, rcx ; ASC + mov rsi, rdx ; Params + + ; Abilities_TryActivateAbility(ASC, Handle*, InputPressed, PredKey*, null) + mov rcx, rbx + lea rdx, [rsi] ; &Params->Handle (at offset 0) + movzx r8d, BYTE PTR [rsi + 8] ; InputPressed (bool at +8) + lea r9, [rsi + 10h] ; &Params->PredictionKey (at +0x10) + call Abilities_TryActivateAbility + + add rsp, 32 + pop rsi + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ServerTryActivateAbility ENDP + +; PEHOOK_ServerTryActivateAbilityWithEventData - includes FGameplayEventData +; Same as above but Params also has FGameplayEventData after PredKey (at +0x28). +; We forward the EventData pointer as the 5th arg. +; +; Stack: push rbp + push rbx + push rsi = 3 pushes; sub 32 -> 0 +PEHOOK_ServerTryActivateAbilityWithEventData PROC + push rbp + push rbx + push rsi + sub rsp, 32 + + mov rbx, rcx + mov rsi, rdx + + mov rcx, rbx + lea rdx, [rsi] + movzx r8d, BYTE PTR [rsi + 8] + lea r9, [rsi + 10h] + ; 5th arg: &EventData at [rsi + 28h] + lea rax, [rsi + 28h] + mov QWORD PTR [rsp + 32], rax + call Abilities_TryActivateAbility + + add rsp, 32 + pop rsi + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ServerTryActivateAbilityWithEventData ENDP + +; PEHOOK_ServerAbilityRPCBatch - parse batch and call TryActivate per entry +; Stub: delegates to TryActivateAbility for the first batch entry. +; +; Stack: push rbp + push rbx + push rsi = 3 pushes; sub 32 -> 0 +PEHOOK_ServerAbilityRPCBatch PROC + push rbp + push rbx + push rsi + sub rsp, 32 + + mov rbx, rcx ; ASC + mov rsi, rdx ; Params (FServerAbilityRPCBatch) + + ; BatchInfo[0].AbilitySpecHandle at [Params+0], PredKey at [Params+8] + ; InputPressed flag at batch entry - assume true (batch implies activation + mov rcx, rbx + lea rdx, [rsi] + mov r8d, 1 ; InputPressed = true + lea r9, [rsi + 8] + call Abilities_TryActivateAbility + + add rsp, 32 + pop rsi + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ServerAbilityRPCBatch ENDP + +; PEHOOK_ClientOnPawnDied - handle death: update state, check win condition +; RCX = AFortPlayerController* (dead player's PC) +; RDX = Params: { FFortPlayerDeathReport DeathReport; } ([RDX+0..+0x4F]) +; +; Simplified implementation: updates DeathInfo on PlayerState, decrements +; PlayersLeft on GameState, calls Game mode's OnPlayerKilled. +; +; Stack: push rbp,rbx,rsi,rdi,r12,r13 = 6 pushes (RSP=8); sub 40 -> 0 +PEHOOK_ClientOnPawnDied PROC + push rbp + push rbx + push rsi + push rdi + push r12 + push r13 + sub rsp, 40 ; 6 pushes: RSP=8; sub40 -> 0 + + mov rbx, rcx ; PC (dead player's controller) + mov r12, rdx ; Params = &FFortPlayerDeathReport + + ; PlayerState = PC->PlayerState + mov rsi, QWORD PTR [rbx + ACONTROLLER_PlayerState] + test rsi, rsi + jz @@done + + ; Copy FDeathInfo from DeathReport into PlayerState->DeathInfo + ; FDeathInfo at AFPSA_DeathInfo offset on PlayerState (0xF40) + ; DeathReport.KillerPlayerState at [Params+0x10] -> DeathInfo.FinisherOrDowner + ; DeathReport.bDBNO flags etc. + mov rax, QWORD PTR [r12 + 010h] ; KillerPlayerState + mov QWORD PTR [rsi + AFPSA_DeathInfo + 000h], rax ; FinisherOrDowner + + ; DeathCause: call Game_GetDeathCause(DeathReport) to compute + mov rcx, r12 + call Game_GetDeathCause ; returns DWORD in EAX + mov BYTE PTR [rsi + AFPSA_DeathInfo + 009h], al ; DeathCause byte + + ; GameState->PlayersLeft-- + call SDK_GetWorld + test rax, rax + jz @@done + ; AuthorityGameMode is on World + mov r13, QWORD PTR [rax + UWORLD_AuthorityGameMode] + + ; TODO: full spectate, victory drone, EndMatch check + +@@done: + add rsp, 40 + pop r13 + pop r12 + pop rdi + pop rsi + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ClientOnPawnDied ENDP + +; PEHOOK_ServerAttemptAircraftJump - jump from battle bus +; RCX = APlayerPawn_Athena_C* (pawn on aircraft) +; RDX = Params: { FRotator ClientRotation; } ([RDX+0]) +; +; Calls GameModeBase_InitPawn to initialise the pawn for landing. +; Stack: push rbp + push rbx + push rsi = 3 pushes; sub 32 -> 0 +PEHOOK_ServerAttemptAircraftJump PROC + push rbp + push rbx + push rsi + sub rsp, 32 + + mov rbx, rcx ; Pawn + mov rsi, rdx ; Params + + ; Get controller from pawn + mov rax, QWORD PTR [rbx + APAWN_Controller] + test rax, rax + jz @@done + + ; GameModeBase_InitPawn(PC) - sets up pawn for first touch-down + mov rcx, rax + call GameModeBase_InitPawn + +@@done: + add rsp, 32 + pop rsi + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ServerAttemptAircraftJump ENDP + +; PEHOOK_OnAircraftExitedDropZone - auto-jump remaining passengers +; RCX = AFortAthenaAircraft* (the aircraft) +; Iterates passengers and forces ServerAttemptAircraftJump on each. +PEHOOK_OnAircraftExitedDropZone PROC + ; TODO: iterate aircraft->Passengers TArray, call jump for each + xor al, al + ret +PEHOOK_OnAircraftExitedDropZone ENDP + +; PEHOOK_ServerCreateBuildingActor - spawn a building piece +PEHOOK_ServerCreateBuildingActor PROC + xor al, al + ret +PEHOOK_ServerCreateBuildingActor ENDP + +; PEHOOK_ServerBeginEditingBuildingActor - start building edit mode +PEHOOK_ServerBeginEditingBuildingActor PROC + xor al, al + ret +PEHOOK_ServerBeginEditingBuildingActor ENDP + +; PEHOOK_ServerEditBuildingActor - apply edit and re-spawn building +PEHOOK_ServerEditBuildingActor PROC + xor al, al + ret +PEHOOK_ServerEditBuildingActor ENDP + +; PEHOOK_ReadyToStartMatch - set up full listen-server infrastructure +; RCX = AFortGameModeAthena* (GameMode) +; +; Sequence: +; 1. Guard bListening - skip if already listening +; 2. Game_OnReadyToStartMatch() +; 3. Spawn AFortOnlineBeaconHost -> HostBeacon +; 4. Set ListenPort=7776 + InitHost +; 5. Init World NetDriver on port 7777 +; 6. Resolve ServerReplicateActors from vtable slot 0x53 +; 7. MaxPlayers=100, PauseBeaconRequests(false), bListening=true +; +; Stack: push rbp,rbx,rsi,rdi,r12,r13,r14 = 7 pushes (RSP=0); sub 80 -> 0 +; [rsp+0..31] = shadow +; [rsp+32..79] = FURL struct (0x70 = 112 bytes)... actually needs sub 128 = 0 +; With 7 pushes: RSP=0; sub 128(=0) -> 0 +; FURL at [rsp+32..143] +PEHOOK_ReadyToStartMatch PROC + push rbp + push rbx + push rsi + push rdi + push r12 + push r13 + push r14 + sub rsp, 128 ; 7 pushes: RSP=0; sub128(=0) -> 0 + ; [rsp+32..143] = FURL local + + mov rbx, rcx ; GameMode + + ; Guard: already listening? + movzx eax, BYTE PTR [bListening] + test al, al + jnz @@done + + ; Game setup + call Game_OnReadyToStartMatch + + ; Spawn AFortOnlineBeaconHost + mov rax, QWORD PTR [pClass_FortOnlineBeaconHost] + test rax, rax + jnz @@have_beacon_class + lea rcx, szClass_FortOnlineBeaconHost + call SDK_FindClass + mov QWORD PTR [pClass_FortOnlineBeaconHost], rax +@@have_beacon_class: + test rax, rax + jz @@net_setup + + ; SpawnActor - use world + class + null transform + default flags + call SDK_GetWorld + test rax, rax + jz @@net_setup + mov r12, rax ; World + + mov rcx, QWORD PTR [pClass_FortOnlineBeaconHost] + xor edx, edx ; Location = null (use zero) + xor r8d, r8d ; Owner = null + call Spawners_SpawnActor + + test rax, rax + jz @@net_setup + mov r13, rax ; r13 = HostBeacon + mov QWORD PTR [HostBeacon], r13 + + ; Set ListenPort = 7776 + mov DWORD PTR [r13 + AOBH_ListenPort], LISTEN_BEACON_PORT + + ; InitHost(HostBeacon) + mov rcx, r13 + call QWORD PTR [Native_OnlineBeaconHost_InitHost] + +@@net_setup: + ; Init World NetDriver on port 7777 + ; Build minimal FURL on stack: zero-init [rsp+32..143], set Port=7777 + lea rcx, [rsp + 32] + xor edx, edx + mov r8d, 112 ; sizeof(FURL) = 0x70 + call RtlZeroMemory + mov DWORD PTR [rsp + 32 + FURL_Port], LISTEN_GAME_PORT ; FURL::Port at +0x20 + + ; Native_NetDriver_InitListen(World?, ??, &FURL, false, &ErrorStr) + + ; Resolve ServerReplicateActors from ReplicationDriver vtable[0x53] + call SDK_GetWorld + test rax, rax + jz @@final_setup + mov rax, QWORD PTR [rax + UWORLD_NetDriver] ; World->NetDriver + test rax, rax + jz @@final_setup + mov rax, QWORD PTR [rax + UNETDRIVER_ReplDriver] ; NetDriver->ReplicationDriver + test rax, rax + jz @@final_setup + + mov r14, rax ; r14 = ReplicationDriver + mov rax, QWORD PTR [r14] ; vtable ptr + mov rax, QWORD PTR [rax + VTABLE_ServerReplicateActors] ; slot 0x53 + mov QWORD PTR [Native_ReplicationDriver_ServerReplicateActors], rax + +@@final_setup: + ; PauseBeaconRequests(false) if beacon was spawned + mov rax, QWORD PTR [HostBeacon] + test rax, rax + jz @@set_listening + mov rcx, rax + xor edx, edx ; false = don't pause + call QWORD PTR [Native_OnlineBeacon_PauseBeaconRequests] + +@@set_listening: + mov BYTE PTR [bListening], 1 + +@@done: + add rsp, 128 + pop r14 + pop r13 + pop r12 + pop rdi + pop rsi + pop rbx + pop rbp + xor al, al + ret +PEHOOK_ReadyToStartMatch ENDP + +; UFunctionHooks_Initialize +; Resolves all 27 UFunction* pointers and fills the ToHook + ToCall arrays. +; Called once from UFunctionHooks_Initialize (triggered before Detours in Main). +; +; Uses rbp as the insertion index (callee-saved, pushed/popped correctly). +; Uses rsi = &_ToHook_Storage[0] +; rdi = &_ToCall_Storage[0] +; +; Stack: push rbp,rbx,rsi,rdi,r12 = 5 pushes (RSP=8); sub 40(=8) -> 0 + +; Helper macro-equivalent - inline for each registration: +; REGISTER rcx=szFnName, handler_label +; lea rcx, szFn_X +; call SDK_FindObject +; test rax,rax / jz skip +; mov [rsi + rbp*8], rax +; lea rax, HANDLER +; mov [rdi + rbp*8], rax +; inc ebp +; skip: + +UFunctionHooks_Initialize PROC + push rbp + push rbx + push rsi + push rdi + push r12 + sub rsp, 40 ; 5 pushes: RSP=8; sub40 -> 0 + + ; Set public pointers to internal storage + lea rax, _ToHook_Storage + mov QWORD PTR [UFunctionHooks_ToHook_Data], rax + lea rax, _ToCall_Storage + mov QWORD PTR [UFunctionHooks_ToCall_Data], rax + + lea rsi, _ToHook_Storage ; hook fn ptr array base + lea rdi, _ToCall_Storage ; handler fn ptr array base + + xor ebp, ebp ; index = 0 + + ; ServerTryActivateAbility + lea rcx, szFn_ServerTryActivateAbility + call SDK_FindObject + test rax, rax + jz @@r1 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerTryActivateAbility + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r1: + ; ServerTryActivateAbilityWithEventData + lea rcx, szFn_ServerTryActivateWithEventData + call SDK_FindObject + test rax, rax + jz @@r2 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerTryActivateAbilityWithEventData + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r2: + ; ServerAbilityRPCBatch + lea rcx, szFn_ServerAbilityRPCBatch + call SDK_FindObject + test rax, rax + jz @@r3 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerAbilityRPCBatch + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r3: + ; ServerHandlePickup + lea rcx, szFn_ServerHandlePickup + call SDK_FindObject + test rax, rax + jz @@r4 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerHandlePickup + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r4: + ; CheatScript + lea rcx, szFn_CheatScript + call SDK_FindObject + test rax, rax + jz @@r5 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_CheatScript + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r5: + ; OnDeathServer + lea rcx, szFn_OnDeathServer + call SDK_FindObject + test rax, rax + jz @@r6 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_OnDeathServer + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r6: + ; ServerCreateBuildingActor + lea rcx, szFn_ServerCreateBuildingActor + call SDK_FindObject + test rax, rax + jz @@r7 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerCreateBuildingActor + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r7: + ; ServerBeginEditingBuildingActor + lea rcx, szFn_ServerBeginEditingBuilding + call SDK_FindObject + test rax, rax + jz @@r8 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerBeginEditingBuildingActor + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r8: + ; ServerSpawnDeco + lea rcx, szFn_ServerSpawnDeco + call SDK_FindObject + test rax, rax + jz @@r9 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerSpawnDeco + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r9: + ; ServerEditBuildingActor + lea rcx, szFn_ServerEditBuildingActor + call SDK_FindObject + test rax, rax + jz @@r10 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerEditBuildingActor + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r10: + ; ClientOnPawnDied + lea rcx, szFn_ClientOnPawnDied + call SDK_FindObject + test rax, rax + jz @@r11 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ClientOnPawnDied + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r11: + ; ServerEndEditingBuildingActor + lea rcx, szFn_ServerEndEditingBuilding + call SDK_FindObject + test rax, rax + jz @@r12 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerEndEditingBuildingActor + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r12: + ; ServerRepairBuildingActor + lea rcx, szFn_ServerRepairBuildingActor + call SDK_FindObject + test rax, rax + jz @@r13 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerRepairBuildingActor + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r13: + ; ServerAttemptAircraftJump + lea rcx, szFn_ServerAttemptAircraftJump + call SDK_FindObject + test rax, rax + jz @@r14 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerAttemptAircraftJump + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r14: + ; ServerReviveFromDBNO + lea rcx, szFn_ServerReviveFromDBNO + call SDK_FindObject + test rax, rax + jz @@r15 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerReviveFromDBNO + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r15: + ; ServerAttemptInteract + lea rcx, szFn_ServerAttemptInteract + call SDK_FindObject + test rax, rax + jz @@r16 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerAttemptInteract + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r16: + ; ServerPlayEmoteItem + lea rcx, szFn_ServerPlayEmoteItem + call SDK_FindObject + test rax, rax + jz @@r17 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerPlayEmoteItem + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r17: + ; ServerAttemptInventoryDrop + lea rcx, szFn_ServerAttemptInventoryDrop + call SDK_FindObject + test rax, rax + jz @@r18 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerAttemptInventoryDrop + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r18: + ; OnSpawnOutAnimEnded + lea rcx, szFn_OnSpawnOutAnimEnded + call SDK_FindObject + test rax, rax + jz @@r19 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_OnSpawnOutAnimEnded + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r19: + ; ServerExecuteInventoryItem + lea rcx, szFn_ServerExecuteInventoryItem + call SDK_FindObject + test rax, rax + jz @@r20 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerExecuteInventoryItem + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r20: + ; ServerReturnToMainMenu + lea rcx, szFn_ServerReturnToMainMenu + call SDK_FindObject + test rax, rax + jz @@r21 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerReturnToMainMenu + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r21: + ; ServerLoadingScreenDropped + lea rcx, szFn_ServerLoadingScreenDropped + call SDK_FindObject + test rax, rax + jz @@r22 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerLoadingScreenDropped + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r22: + ; ServerChoosePart + lea rcx, szFn_ServerChoosePart + call SDK_FindObject + test rax, rax + jz @@r23 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerChoosePart + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r23: + ; ReadyToStartMatch + lea rcx, szFn_ReadyToStartMatch + call SDK_FindObject + test rax, rax + jz @@r24 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ReadyToStartMatch + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r24: + ; OnAircraftExitedDropZone + lea rcx, szFn_OnAircraftExitedDropZone + call SDK_FindObject + test rax, rax + jz @@r25 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_OnAircraftExitedDropZone + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r25: + ; ServerCheatAll + lea rcx, szFn_ServerCheatAll + call SDK_FindObject + test rax, rax + jz @@r26 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_ServerCheatAll + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r26: + ; Logout + lea rcx, szFn_Logout + call SDK_FindObject + test rax, rax + jz @@r27 + mov QWORD PTR [rsi + rbp * 8], rax + lea rax, PEHOOK_Logout + mov QWORD PTR [rdi + rbp * 8], rax + inc ebp +@@r27: + + ; Store final count + mov DWORD PTR [UFunctionHooks_ToHook_Num], ebp + + ; Also lazily prime the ForceNetUpdate UFunction pointer + lea rcx, szFn_ForceNetUpdate + call SDK_FindObject + mov QWORD PTR [pFn_ForceNetUpdate], rax + + ; Log registration count + lea rcx, szHookCount + mov edx, ebp + call Logger_LogInfoFmt + + add rsp, 40 + pop r12 + pop rdi + pop rsi + pop rbx + pop rbp + ret +UFunctionHooks_Initialize ENDP + +END \ No newline at end of file diff --git a/util.asm b/util.asm index 8bfcb5c..53fe8bc 100644 --- a/util.asm +++ b/util.asm @@ -108,7 +108,7 @@ Utils_FindPattern PROC sub r14d, esi ; r14d = SizeOfImage - pat_len js @@not_found ; underflow -> image too small - ; r12d = pat_len (repurpose r12 — pszPattern no longer needed) + ; 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