From 58bd34050171b9b7df2d051de7f5e46cbd615d44 Mon Sep 17 00:00:00 2001 From: Milxnor Date: Sun, 23 Apr 2023 11:04:06 -0400 Subject: [PATCH] idek remove s18+ storm effect, fix teams on all versions, fix a crash, fix 1.11 restarting --- Project Reboot 3.0/BuildingFoundation.cpp | 3 +- .../FortAthenaMutator_InventoryOverride.h | 19 +++-- Project Reboot 3.0/FortGameModeAthena.cpp | 49 ++++++------- Project Reboot 3.0/FortGameModeAthena.h | 57 ++++++++++----- Project Reboot 3.0/FortPlayerController.cpp | 9 ++- .../FortPlayerControllerAthena.cpp | 14 +++- Project Reboot 3.0/GameMode.cpp | 4 +- Project Reboot 3.0/GameModeBase.cpp | 7 -- Project Reboot 3.0/GenericPlatformMath.cpp | 73 +++++++++++++++++++ Project Reboot 3.0/GenericPlatformMath.h | 21 +++++- Project Reboot 3.0/NetDriver.cpp | 23 +++--- Project Reboot 3.0/Project Reboot 3.0.vcxproj | 2 + .../Project Reboot 3.0.vcxproj.filters | 9 +++ Project Reboot 3.0/Quat.h | 2 + Project Reboot 3.0/Rotator.h | 35 ++++++++- Project Reboot 3.0/UnrealMath.cpp | 42 ++++++++++- Project Reboot 3.0/UnrealMathUtility.h | 34 ++++++++- Project Reboot 3.0/addresses.cpp | 2 + Project Reboot 3.0/calendar.h | 53 ++++++++++++++ Project Reboot 3.0/commands.h | 19 +++++ Project Reboot 3.0/dllmain.cpp | 3 +- Project Reboot 3.0/finder.h | 2 +- Project Reboot 3.0/gui.h | 64 +++++++++++++--- Project Reboot 3.0/hooking.h | 53 +++++++++++--- 24 files changed, 500 insertions(+), 99 deletions(-) create mode 100644 Project Reboot 3.0/GenericPlatformMath.cpp create mode 100644 Project Reboot 3.0/calendar.h diff --git a/Project Reboot 3.0/BuildingFoundation.cpp b/Project Reboot 3.0/BuildingFoundation.cpp index 67dc390..09f0923 100644 --- a/Project Reboot 3.0/BuildingFoundation.cpp +++ b/Project Reboot 3.0/BuildingFoundation.cpp @@ -10,8 +10,7 @@ void ABuildingFoundation::SetDynamicFoundationTransformHook(UObject* Context, FF LOG_INFO(LogDev, "Bruh: {}", BuildingFoundation->GetName()); - static auto DynamicFoundationTransformOffset = BuildingFoundation->GetOffset("DynamicFoundationTransform"); - BuildingFoundation->Get(DynamicFoundationTransformOffset) = NewTransform; + SetFoundationTransform(BuildingFoundation, NewTransform); return SetDynamicFoundationTransformOriginal(Context, Stack, Ret); } diff --git a/Project Reboot 3.0/FortAthenaMutator_InventoryOverride.h b/Project Reboot 3.0/FortAthenaMutator_InventoryOverride.h index a900b26..324bfcb 100644 --- a/Project Reboot 3.0/FortAthenaMutator_InventoryOverride.h +++ b/Project Reboot 3.0/FortAthenaMutator_InventoryOverride.h @@ -44,19 +44,26 @@ public: return Get>(InventoryLoadoutsOffset); } - TArray& GetTeamLoadouts() + TArray* GetTeamLoadouts() { - static auto TeamLoadoutsOffset = GetOffset("TeamLoadouts"); - return Get>(TeamLoadoutsOffset); + static auto TeamLoadoutsOffset = GetOffset("TeamLoadouts", false); + + if (TeamLoadoutsOffset == -1) + return nullptr; + + return GetPtr>(TeamLoadoutsOffset); } FItemLoadoutTeamMap GetLoadoutTeamForTeamIndex(uint8_t TeamIndex) { - auto& TeamLoadouts = GetTeamLoadouts(); + auto TeamLoadouts = GetTeamLoadouts(); - for (int i = 0; i < TeamLoadouts.Num(); i++) + if (!TeamLoadouts) + return FItemLoadoutTeamMap(); + + for (int i = 0; i < TeamLoadouts->Num(); i++) { - auto& TeamLoadout = TeamLoadouts.at(i); + auto& TeamLoadout = TeamLoadouts->at(i); if (TeamLoadout.TeamIndex == TeamIndex) return TeamLoadout; diff --git a/Project Reboot 3.0/FortGameModeAthena.cpp b/Project Reboot 3.0/FortGameModeAthena.cpp index b1abd4c..be5038a 100644 --- a/Project Reboot 3.0/FortGameModeAthena.cpp +++ b/Project Reboot 3.0/FortGameModeAthena.cpp @@ -25,6 +25,7 @@ #include "BGA.h" #include "vendingmachine.h" #include "FortAthenaMutator.h" +#include "calendar.h" static UFortPlaylist* GetPlaylistToUse() { @@ -221,11 +222,18 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game { if (Fortnite_Version == 7.30) { - auto PleasantParkIdk = FindObject(("/Game/Athena/Maps/Athena_POI_Foundations.Athena_POI_Foundations.PersistentLevel.PleasentParkFestivus")); - ShowFoundation(PleasantParkIdk); - - auto PleasantParkGround = FindObject("/Game/Athena/Maps/Athena_POI_Foundations.Athena_POI_Foundations.PersistentLevel.PleasentParkDefault"); - ShowFoundation(PleasantParkGround); + // should be automatic.. + + if (true) + { + auto PleasantParkIdk = FindObject(("/Game/Athena/Maps/Athena_POI_Foundations.Athena_POI_Foundations.PersistentLevel.PleasentParkFestivus")); + ShowFoundation(PleasantParkIdk); + } + else + { + auto PleasantParkGround = FindObject("/Game/Athena/Maps/Athena_POI_Foundations.Athena_POI_Foundations.PersistentLevel.PleasentParkDefault"); + ShowFoundation(PleasantParkGround); + } } ShowFoundation(FindObject("/Game/Athena/Maps/Athena_POI_Foundations.Athena_POI_Foundations.PersistentLevel.LF_Athena_POI_25x36")); // Polar Peak @@ -246,9 +254,6 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game ShowFoundation(Island); } } - - auto TheBlock = FindObject("/Game/Athena/Maps/Athena_POI_Foundations.Athena_POI_Foundations.PersistentLevel.SLAB_2"); // SLAB_3 is blank - ShowFoundation(TheBlock); } if (Fortnite_Version == 17.50) { @@ -402,6 +407,9 @@ bool AFortGameModeAthena::Athena_ReadyToStartMatchHook(AFortGameModeAthena* Game } SetBitfield(GameMode->GetPtr("bWorldIsReady"), 1, true); // idk when we actually set this + + // Calendar::SetSnow(1000); + Globals::bInitializedPlaylist = true; } @@ -649,10 +657,13 @@ int AFortGameModeAthena::Athena_PickTeamHook(AFortGameModeAthena* GameMode, uint static int DefaultFirstTeam = 3; + bool bShouldSpreadTeams = false; + if (Playlist) { static auto bIsLargeTeamGameOffset = Playlist->GetOffset("bIsLargeTeamGame"); bool bIsLargeTeamGame = Playlist->Get(bIsLargeTeamGameOffset); + bShouldSpreadTeams = bIsLargeTeamGame; } static int CurrentTeamMembers = 0; // bad @@ -673,8 +684,6 @@ int AFortGameModeAthena::Athena_PickTeamHook(AFortGameModeAthena* GameMode, uint int MaxSquadSize = 1; int TeamsNum = 0; - bool bShouldSpreadTeams = false; - if (bVersionHasPlaylist) { if (!Playlist) @@ -691,7 +700,9 @@ int AFortGameModeAthena::Athena_PickTeamHook(AFortGameModeAthena* GameMode, uint static auto bShouldSpreadTeamsOffset = Playlist->GetOffset("bShouldSpreadTeams", false); if (bShouldSpreadTeamsOffset != -1) - bShouldSpreadTeams = Playlist->Get(bShouldSpreadTeamsOffset); + { + // bShouldSpreadTeams = Playlist->Get(bShouldSpreadTeamsOffset); + } static auto MaxTeamCountOffset = Playlist->GetOffset("MaxTeamCount", false); @@ -710,18 +721,6 @@ int AFortGameModeAthena::Athena_PickTeamHook(AFortGameModeAthena* GameMode, uint } static int NextTeamIndex = DefaultFirstTeam; - static int LastTeamIndex = NextTeamIndex; - - static int LastNum1 = 1; - - if (AmountOfRestarts != LastNum1) - { - LastNum1 = AmountOfRestarts; - - NextTeamIndex = DefaultFirstTeam; - } - - LastTeamIndex = NextTeamIndex; if (!bShouldSpreadTeams) { @@ -748,7 +747,7 @@ int AFortGameModeAthena::Athena_PickTeamHook(AFortGameModeAthena* GameMode, uint } } - LOG_INFO(LogTeams, "Player is going on team {} with {} members.", NextTeamIndex, CurrentTeamMembers); + LOG_INFO(LogTeams, "Spreading Teams {} Player is going on team {} with {} members.", bShouldSpreadTeams, NextTeamIndex, CurrentTeamMembers); CurrentTeamMembers++; @@ -888,7 +887,7 @@ void AFortGameModeAthena::Athena_HandleStartingNewPlayerHook(AFortGameModeAthena } } - if (Engine_Version >= 423 && Fortnite_Version <= 12.41) // 423+ we need to spawn manually and vehicle sync doesn't work on >S13. + if (Engine_Version >= 423 && Fortnite_Version <= 12.61) // 423+ we need to spawn manually and vehicle sync doesn't work on >S13. { static int LastNum420 = 1; diff --git a/Project Reboot 3.0/FortGameModeAthena.h b/Project Reboot 3.0/FortGameModeAthena.h index 34865b7..fd5e7e1 100644 --- a/Project Reboot 3.0/FortGameModeAthena.h +++ b/Project Reboot 3.0/FortGameModeAthena.h @@ -41,6 +41,42 @@ struct FAircraftFlightInfo } }; +static void SetFoundationTransform(AActor* BuildingFoundation, const FTransform& Transform) +{ + static auto DynamicFoundationRepDataOffset = BuildingFoundation->GetOffset("DynamicFoundationRepData", false); + + static auto Enabled = 1; + static auto Disabled = 2; + + static auto DynamicFoundationTransformOffset = BuildingFoundation->GetOffset("DynamicFoundationTransform", false); + + if (DynamicFoundationTransformOffset != -1) // needed check? + { + auto DynamicFoundationTransform = BuildingFoundation->GetPtr(DynamicFoundationTransformOffset); + + *DynamicFoundationTransform = Transform; + } + + if (DynamicFoundationRepDataOffset != -1) + { + auto DynamicFoundationRepData = BuildingFoundation->GetPtr(DynamicFoundationRepDataOffset); + + static auto RotationOffset = FindOffsetStruct("/Script/FortniteGame.DynamicBuildingFoundationRepData", "Rotation"); + static auto TranslationOffset = FindOffsetStruct("/Script/FortniteGame.DynamicBuildingFoundationRepData", "Translation"); + + if (DynamicFoundationTransformOffset != -1) // needed check? + { + auto DynamicFoundationTransform = BuildingFoundation->GetPtr(DynamicFoundationTransformOffset); + + *(FRotator*)(__int64(DynamicFoundationRepData) + RotationOffset) = DynamicFoundationTransform->Rotation.Rotator(); + *(FVector*)(__int64(DynamicFoundationRepData) + TranslationOffset) = DynamicFoundationTransform->Translation; + } + + static auto OnRep_DynamicFoundationRepDataFn = FindObject("/Script/FortniteGame.BuildingFoundation.OnRep_DynamicFoundationRepData"); + BuildingFoundation->ProcessEvent(OnRep_DynamicFoundationRepDataFn); + } +} + static void ShowFoundation(AActor* BuildingFoundation, bool bShow = true) { if (!BuildingFoundation) @@ -74,13 +110,13 @@ static void ShowFoundation(AActor* BuildingFoundation, bool bShow = true) static auto DynamicFoundationTypeOffset = BuildingFoundation->GetOffset("DynamicFoundationType"); BuildingFoundation->Get(DynamicFoundationTypeOffset) = bShow ? Static : StartDisabled; - static auto bShowHLODWhenDisabledOffset = BuildingFoundation->GetOffset("bShowHLODWhenDisabled", false); + /* static auto bShowHLODWhenDisabledOffset = BuildingFoundation->GetOffset("bShowHLODWhenDisabled", false); if (bShowHLODWhenDisabledOffset != -1) { static auto bShowHLODWhenDisabledFieldMask = GetFieldMask(BuildingFoundation->GetProperty("bShowHLODWhenDisabled")); BuildingFoundation->SetBitfieldValue(bShowHLODWhenDisabledOffset, bShowHLODWhenDisabledFieldMask, true); - } + } */ static auto OnRep_ServerStreamedInLevelFn = FindObject("/Script/FortniteGame.BuildingFoundation.OnRep_ServerStreamedInLevel"); BuildingFoundation->ProcessEvent(OnRep_ServerStreamedInLevelFn); @@ -90,17 +126,6 @@ static void ShowFoundation(AActor* BuildingFoundation, bool bShow = true) static auto Enabled = 1; static auto Disabled = 2; - static auto DynamicFoundationTransformOffset = BuildingFoundation->GetOffset("DynamicFoundationTransform", false); - - if (DynamicFoundationTransformOffset != -1) // needed check? - { - auto DynamicFoundationTransform = BuildingFoundation->GetPtr(DynamicFoundationTransformOffset); - - DynamicFoundationTransform->Rotation = BuildingFoundation->GetActorRotation().Quaternion(); - DynamicFoundationTransform->Translation = BuildingFoundation->GetActorLocation(); - DynamicFoundationTransform->Scale3D = BuildingFoundation->GetActorScale3D(); - } - if (DynamicFoundationRepDataOffset != -1) { auto DynamicFoundationRepData = BuildingFoundation->GetPtr(DynamicFoundationRepDataOffset); @@ -110,10 +135,6 @@ static void ShowFoundation(AActor* BuildingFoundation, bool bShow = true) static auto EnabledStateOffset = FindOffsetStruct("/Script/FortniteGame.DynamicBuildingFoundationRepData", "EnabledState"); *(uint8_t*)(__int64(DynamicFoundationRepData) + EnabledStateOffset) = bShow ? Enabled : Disabled; - - // hmm - *(FRotator*)(__int64(DynamicFoundationRepData) + RotationOffset) = BuildingFoundation->GetActorRotation(); - *(FVector*)(__int64(DynamicFoundationRepData) + TranslationOffset) = BuildingFoundation->GetActorLocation(); static auto OnRep_DynamicFoundationRepDataFn = FindObject("/Script/FortniteGame.BuildingFoundation.OnRep_DynamicFoundationRepData"); BuildingFoundation->ProcessEvent(OnRep_DynamicFoundationRepDataFn); @@ -124,6 +145,8 @@ static void ShowFoundation(AActor* BuildingFoundation, bool bShow = true) if (FoundationEnabledStateOffset != -1) BuildingFoundation->Get(FoundationEnabledStateOffset) = bShow ? Enabled : Disabled; + SetFoundationTransform(BuildingFoundation, BuildingFoundation->GetTransform()); // idk + static auto LevelToStreamOffset = BuildingFoundation->GetOffset("LevelToStream"); auto& LevelToStream = BuildingFoundation->Get(LevelToStreamOffset); diff --git a/Project Reboot 3.0/FortPlayerController.cpp b/Project Reboot 3.0/FortPlayerController.cpp index 2eebb5d..1e7de0d 100644 --- a/Project Reboot 3.0/FortPlayerController.cpp +++ b/Project Reboot 3.0/FortPlayerController.cpp @@ -565,7 +565,7 @@ void AFortPlayerController::ServerAttemptInteractHook(UObject* Context, FFrame* void AFortPlayerController::ServerAttemptAircraftJumpHook(AFortPlayerController* PC, FRotator ClientRotation) { - auto PlayerController = Cast(Engine_Version < 424 ? PC : ((UActorComponent*)PC)->GetOwner()); + auto PlayerController = Cast(Engine_Version < 424 ? PC : ((UActorComponent*)PC)->GetOwner()); if (Engine_Version < 424 && !Globals::bLateGame.load()) return ServerAttemptAircraftJumpOriginal(PC, ClientRotation); @@ -595,6 +595,13 @@ void AFortPlayerController::ServerAttemptAircraftJumpHook(AFortPlayerController* auto NewPawnAsFort = Cast(NewPawn); + if (Fortnite_Version >= 18) + { + static auto StormEffectClass = FindObject("/Game/Athena/SafeZone/GE_OutsideSafeZoneDamage.GE_OutsideSafeZoneDamage_C"); + auto PlayerState = PlayerController->GetPlayerStateAthena(); + PlayerState->GetAbilitySystemComponent()->RemoveActiveGameplayEffectBySourceEffect(StormEffectClass, 1, PlayerState->GetAbilitySystemComponent()); + } + if (NewPawnAsFort) { NewPawnAsFort->SetHealth(100); diff --git a/Project Reboot 3.0/FortPlayerControllerAthena.cpp b/Project Reboot 3.0/FortPlayerControllerAthena.cpp index 1ab4119..a712f23 100644 --- a/Project Reboot 3.0/FortPlayerControllerAthena.cpp +++ b/Project Reboot 3.0/FortPlayerControllerAthena.cpp @@ -24,7 +24,9 @@ void AFortPlayerControllerAthena::StartGhostModeHook(UObject* Context, FFrame* S // if (!Controller->HasAuthority) return StartGhostModeOriginal(Context, Stack, Ret); - // if (Controller->GhostModeRepData.bInGhostMode) return StartGhostModeOriginal(Context, Stack, Ret); + auto GhostModeRepData = Controller->GetGhostModeRepData(); + + if (GhostModeRepData->IsInGhostMode()) return StartGhostModeOriginal(Context, Stack, Ret); auto WorldInventory = Controller->GetWorldInventory(); @@ -217,6 +219,16 @@ void AFortPlayerControllerAthena::EnterAircraftHook(UObject* PC, AActor* Aircraf WorldInventory->Update(); // Should we equip the pickaxe for older builds here? + + if (Fortnite_Version < 2.5) // idk + { + /* auto PickaxeInstance = WorldInventory->GetPickaxeInstance(); + + if (!PickaxeInstance) + return; + + AFortPlayerController::ServerExecuteInventoryItemHook(PlayerController, PickaxeInstance->GetItemEntry()->GetItemGuid()); */ + } } void AFortPlayerControllerAthena::ServerRequestSeatChangeHook(AFortPlayerControllerAthena* PlayerController, int TargetSeatIndex) diff --git a/Project Reboot 3.0/GameMode.cpp b/Project Reboot 3.0/GameMode.cpp index f2c10b9..16a02fb 100644 --- a/Project Reboot 3.0/GameMode.cpp +++ b/Project Reboot 3.0/GameMode.cpp @@ -4,6 +4,6 @@ void AGameMode::RestartGame() { - static auto fn = FindObject("/Script/Engine.GameMode.RestartGame"); - this->ProcessEvent(fn); + static auto RestartGameFn = FindObject("/Script/Engine.GameMode.RestartGame"); + this->ProcessEvent(RestartGameFn); } \ No newline at end of file diff --git a/Project Reboot 3.0/GameModeBase.cpp b/Project Reboot 3.0/GameModeBase.cpp index 8b1b06d..8975267 100644 --- a/Project Reboot 3.0/GameModeBase.cpp +++ b/Project Reboot 3.0/GameModeBase.cpp @@ -79,13 +79,6 @@ APawn* AGameModeBase::SpawnDefaultPawnForHook(AGameModeBase* GameMode, AControll CurrentPlaylist->ApplyModifiersToActor(PlayerStateAthena); // We need to move this! } - /* if (Fortnite_Version >= 18) - { - static auto StormEffectClass = FindObject("/Game/Athena/SafeZone/GE_OutsideSafeZoneDamage.GE_OutsideSafeZoneDamage_C"); - auto PlayerState = NewPlayerAsAthena->GetPlayerStateAthena(); - PlayerState->GetAbilitySystemComponent()->RemoveActiveGameplayEffectBySourceEffect(StormEffectClass, 1, PlayerState->GetAbilitySystemComponent()); - } */ - if (NewPlayerAsAthena) { auto WorldInventory = NewPlayerAsAthena->GetWorldInventory(); diff --git a/Project Reboot 3.0/GenericPlatformMath.cpp b/Project Reboot 3.0/GenericPlatformMath.cpp new file mode 100644 index 0000000..1ac4280 --- /dev/null +++ b/Project Reboot 3.0/GenericPlatformMath.cpp @@ -0,0 +1,73 @@ +#include "GenericPlatformMath.h" + +#include "UnrealMathUtility.h" + +#define PI (3.1415926535897932f) + +/*FORCENOINLINE*/ float FGenericPlatformMath::Fmod(float X, float Y) +{ + if (fabsf(Y) <= 1.e-8f) + { + // FmodReportError(X, Y); + return 0.f; + } + const float Div = (X / Y); + // All floats where abs(f) >= 2^23 (8388608) are whole numbers so do not need truncation, and avoid overflow in TruncToFloat as they get even larger. + const float Quotient = fabsf(Div) < 8388608.f ? TruncToFloat(Div) : Div; + float IntPortion = Y * Quotient; + + // Rounding and imprecision could cause IntPortion to exceed X and cause the result to be outside the expected range. + // For example Fmod(55.8, 9.3) would result in a very small negative value! + if (fabsf(IntPortion) > fabsf(X)) + { + IntPortion = X; + } + + const float Result = X - IntPortion; + return Result; +} + +float FGenericPlatformMath::Atan2(float Y, float X) +{ + //return atan2f(Y,X); + // atan2f occasionally returns NaN with perfectly valid input (possibly due to a compiler or library bug). + // We are replacing it with a minimax approximation with a max relative error of 7.15255737e-007 compared to the C library function. + // On PC this has been measured to be 2x faster than the std C version. + + const float absX = FMath::Abs(X); + const float absY = FMath::Abs(Y); + const bool yAbsBigger = (absY > absX); + float t0 = yAbsBigger ? absY : absX; // Max(absY, absX) + float t1 = yAbsBigger ? absX : absY; // Min(absX, absY) + + if (t0 == 0.f) + return 0.f; + + float t3 = t1 / t0; + float t4 = t3 * t3; + + static const float c[7] = { + +7.2128853633444123e-03f, + -3.5059680836411644e-02f, + +8.1675882859940430e-02f, + -1.3374657325451267e-01f, + +1.9856563505717162e-01f, + -3.3324998579202170e-01f, + +1.0f + }; + + t0 = c[0]; + t0 = t0 * t4 + c[1]; + t0 = t0 * t4 + c[2]; + t0 = t0 * t4 + c[3]; + t0 = t0 * t4 + c[4]; + t0 = t0 * t4 + c[5]; + t0 = t0 * t4 + c[6]; + t3 = t0 * t3; + + t3 = yAbsBigger ? (0.5f * PI) - t3 : t3; + t3 = (X < 0.0f) ? PI - t3 : t3; + t3 = (Y < 0.0f) ? -t3 : t3; + + return t3; +} \ No newline at end of file diff --git a/Project Reboot 3.0/GenericPlatformMath.h b/Project Reboot 3.0/GenericPlatformMath.h index 5fa9080..95439f0 100644 --- a/Project Reboot 3.0/GenericPlatformMath.h +++ b/Project Reboot 3.0/GenericPlatformMath.h @@ -2,7 +2,7 @@ #include "inc.h" -class FPlatformMath +class FGenericPlatformMath { public: static constexpr FORCEINLINE int32 TruncToInt(float F) @@ -21,6 +21,8 @@ public: return (A <= B) ? A : B; } + static FORCENOINLINE float Fmod(float X, float Y); + static FORCEINLINE int32 FloorToInt(float F) { return TruncToInt(floorf(F)); @@ -40,6 +42,23 @@ public: return (A >= B) ? A : B; } + static FORCEINLINE float Sin(float Value) { return sinf(Value); } + static FORCEINLINE float Asin(float Value) { return asinf((Value < -1.f) ? -1.f : ((Value < 1.f) ? Value : 1.f)); } + static FORCEINLINE float Sinh(float Value) { return sinhf(Value); } + static FORCEINLINE float Cos(float Value) { return cosf(Value); } + static FORCEINLINE float Acos(float Value) { return acosf((Value < -1.f) ? -1.f : ((Value < 1.f) ? Value : 1.f)); } + static FORCEINLINE float Tan(float Value) { return tanf(Value); } + static FORCEINLINE float Atan(float Value) { return atanf(Value); } + static float Atan2(float Y, float X); + static FORCEINLINE float Sqrt(float Value) { return sqrtf(Value); } + static FORCEINLINE float Pow(float A, float B) { return powf(A, B); } + + template< class T > + static constexpr FORCEINLINE T Abs(const T A) + { + return (A >= (T)0) ? A : -A; + } + static FORCEINLINE float FloorToFloat(float F) { return floorf(F); diff --git a/Project Reboot 3.0/NetDriver.cpp b/Project Reboot 3.0/NetDriver.cpp index b2e027b..292838d 100644 --- a/Project Reboot 3.0/NetDriver.cpp +++ b/Project Reboot 3.0/NetDriver.cpp @@ -22,16 +22,19 @@ void UNetDriver::RemoveNetworkActor(AActor* Actor) void UNetDriver::TickFlushHook(UNetDriver* NetDriver) { - static auto ReplicationDriverOffset = NetDriver->GetOffset("ReplicationDriver", false); + if (Globals::bStartedListening) + { + static auto ReplicationDriverOffset = NetDriver->GetOffset("ReplicationDriver", false); - if (ReplicationDriverOffset == -1) - { - NetDriver->ServerReplicateActors(); - } - else - { - if (auto ReplicationDriver = NetDriver->Get(ReplicationDriverOffset)) - reinterpret_cast(ReplicationDriver->VFTable[Offsets::ServerReplicateActors])(ReplicationDriver); + if (ReplicationDriverOffset == -1) + { + NetDriver->ServerReplicateActors(); + } + else + { + if (auto ReplicationDriver = NetDriver->Get(ReplicationDriverOffset)) + reinterpret_cast(ReplicationDriver->VFTable[Offsets::ServerReplicateActors])(ReplicationDriver); + } } return TickFlushOriginal(NetDriver); @@ -300,7 +303,7 @@ static bool IsActorRelevantToConnection(AActor * Actor, std::vector& return false; } -static FNetViewer ConstructNetViewer(UNetConnection * NetConnection) +static FNetViewer ConstructNetViewer(UNetConnection* NetConnection) { FNetViewer newViewer{}; newViewer.Connection = NetConnection; diff --git a/Project Reboot 3.0/Project Reboot 3.0.vcxproj b/Project Reboot 3.0/Project Reboot 3.0.vcxproj index 5c478a8..5c52850 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj @@ -183,6 +183,7 @@ + @@ -229,6 +230,7 @@ + diff --git a/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters b/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters index 50f2774..7f3b257 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters @@ -245,6 +245,12 @@ FortniteGame\Source\FortniteGame\Private\Mutators + + Engine\Source\Runtime\Core\Private\GenericPlatform + + + Reboot\Public + @@ -1033,6 +1039,9 @@ {3d909143-220c-44b3-819f-79d282c3fc0f} + + {c04eb59f-e186-49a3-a145-1fd3dc1dcd3d} + diff --git a/Project Reboot 3.0/Quat.h b/Project Reboot 3.0/Quat.h index 479a4b3..db4d242 100644 --- a/Project Reboot 3.0/Quat.h +++ b/Project Reboot 3.0/Quat.h @@ -17,4 +17,6 @@ public: /** The quaternion's W-component. */ float W; + + struct FRotator Rotator() const; }; \ No newline at end of file diff --git a/Project Reboot 3.0/Rotator.h b/Project Reboot 3.0/Rotator.h index c8f38b1..0d04158 100644 --- a/Project Reboot 3.0/Rotator.h +++ b/Project Reboot 3.0/Rotator.h @@ -3,6 +3,8 @@ #include "Quat.h" #include "Vector.h" +#include "UnrealMathUtility.h" + struct FRotator { float Pitch; @@ -12,4 +14,35 @@ struct FRotator FQuat Quaternion(); FVector Vector() const; -}; \ No newline at end of file + + static float NormalizeAxis(float Angle); + static float ClampAxis(float Angle); +}; + +FORCEINLINE float FRotator::ClampAxis(float Angle) +{ + // returns Angle in the range (-360,360) + Angle = FMath::Fmod(Angle, 360.f); + + if (Angle < 0.f) + { + // shift to [0,360) range + Angle += 360.f; + } + + return Angle; +} + +FORCEINLINE float FRotator::NormalizeAxis(float Angle) +{ + // returns Angle in the range [0,360) + Angle = ClampAxis(Angle); + + if (Angle > 180.f) + { + // shift to (-180,180] + Angle -= 360.f; + } + + return Angle; +} \ No newline at end of file diff --git a/Project Reboot 3.0/UnrealMath.cpp b/Project Reboot 3.0/UnrealMath.cpp index 2f2e4b9..7b4c0ac 100644 --- a/Project Reboot 3.0/UnrealMath.cpp +++ b/Project Reboot 3.0/UnrealMath.cpp @@ -1,4 +1,6 @@ #include "Rotator.h" +#include "Quat.h" +#include "UnrealMathUtility.h" #define INV_PI (0.31830988618f) #define HALF_PI (1.57079632679f) @@ -47,7 +49,6 @@ static FORCEINLINE void SinCos(float* ScalarSin, float* ScalarCos, float Value) struct FQuat FRotator::Quaternion() { - #if PLATFORM_ENABLE_VECTORINTRINSICS const VectorRegister Angles = MakeVectorRegister(Rotator.Pitch, Rotator.Yaw, Rotator.Roll, 0.0f); const VectorRegister HalfAngles = VectorMultiply(Angles, DEG_TO_RAD_HALF); @@ -111,4 +112,43 @@ FVector FRotator::Vector() const FVector V = FVector(CP * CY, CP * SY, SP); return V; +} + +FRotator FQuat::Rotator() const +{ + const float SingularityTest = Z * X - W * Y; + const float YawY = 2.f * (W * Z + X * Y); + const float YawX = (1.f - 2.f * (FMath::Square(Y) + FMath::Square(Z))); + + // reference + // http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/ + + // this value was found from experience, the above websites recommend different values + // but that isn't the case for us, so I went through different testing, and finally found the case + // where both of world lives happily. + const float SINGULARITY_THRESHOLD = 0.4999995f; + const float RAD_TO_DEG = (180.f) / PI; + FRotator RotatorFromQuat; + + if (SingularityTest < -SINGULARITY_THRESHOLD) + { + RotatorFromQuat.Pitch = -90.f; + RotatorFromQuat.Yaw = FMath::Atan2(YawY, YawX) * RAD_TO_DEG; + RotatorFromQuat.Roll = FRotator::NormalizeAxis(-RotatorFromQuat.Yaw - (2.f * FMath::Atan2(X, W) * RAD_TO_DEG)); + } + else if (SingularityTest > SINGULARITY_THRESHOLD) + { + RotatorFromQuat.Pitch = 90.f; + RotatorFromQuat.Yaw = FMath::Atan2(YawY, YawX) * RAD_TO_DEG; + RotatorFromQuat.Roll = FRotator::NormalizeAxis(RotatorFromQuat.Yaw - (2.f * FMath::Atan2(X, W) * RAD_TO_DEG)); + } + else + { + RotatorFromQuat.Pitch = FMath::FastAsin(2.f * (SingularityTest)) * RAD_TO_DEG; + RotatorFromQuat.Yaw = FMath::Atan2(YawY, YawX) * RAD_TO_DEG; + RotatorFromQuat.Roll = FMath::Atan2(-2.f * (W * X + Y * Z), (1.f - 2.f * (FMath::Square(X) + FMath::Square(Y)))) * RAD_TO_DEG; + } + + return RotatorFromQuat; } \ No newline at end of file diff --git a/Project Reboot 3.0/UnrealMathUtility.h b/Project Reboot 3.0/UnrealMathUtility.h index 2354654..41660ab 100644 --- a/Project Reboot 3.0/UnrealMathUtility.h +++ b/Project Reboot 3.0/UnrealMathUtility.h @@ -2,11 +2,43 @@ #include "GenericPlatformMath.h" -struct FMath : public FPlatformMath +struct FMath : public FGenericPlatformMath { template< class T > static FORCEINLINE T Clamp(const T X, const T Min, const T Max) { return X < Min ? Min : X < Max ? X : Max; } + + template< class T > + static FORCEINLINE T Square(const T A) + { + return A * A; + } + +#define FASTASIN_HALF_PI (1.5707963050f) + /** + * Computes the ASin of a scalar value. + * + * @param Value input angle + * @return ASin of Value + */ + static FORCEINLINE float FastAsin(float Value) + { + // Clamp input to [-1,1]. + bool nonnegative = (Value >= 0.0f); + float x = FMath::Abs(Value); + float omx = 1.0f - x; + if (omx < 0.0f) + { + omx = 0.0f; + } + float root = FMath::Sqrt(omx); + // 7-degree minimax approximation + float result = ((((((-0.0012624911f * x + 0.0066700901f) * x - 0.0170881256f) * x + 0.0308918810f) * x - 0.0501743046f) * x + 0.0889789874f) * x - 0.2145988016f) * x + FASTASIN_HALF_PI; + result *= root; // acos(|x|) + // acos(x) = pi - acos(-x) when x < 0, asin(x) = pi/2 - acos(x) + return (nonnegative ? FASTASIN_HALF_PI - result : result - FASTASIN_HALF_PI); + } +#undef FASTASIN_HALF_PI }; \ No newline at end of file diff --git a/Project Reboot 3.0/addresses.cpp b/Project Reboot 3.0/addresses.cpp index bdaae5b..f75c1fb 100644 --- a/Project Reboot 3.0/addresses.cpp +++ b/Project Reboot 3.0/addresses.cpp @@ -98,6 +98,8 @@ void Addresses::SetupVersion() Fortnite_Version = 1.8; if (Fortnite_CL == 3870737) Fortnite_Version = 2.42; + + toFree.Free(); } void Addresses::FindAll() diff --git a/Project Reboot 3.0/calendar.h b/Project Reboot 3.0/calendar.h new file mode 100644 index 0000000..5bb3c31 --- /dev/null +++ b/Project Reboot 3.0/calendar.h @@ -0,0 +1,53 @@ +#pragma once + +#include "reboot.h" +#include "GameplayStatics.h" +#include "FortGameStateAthena.h" + +namespace Calendar +{ + static inline bool HasSnowModification() + { + return Fortnite_Version == 7.30; + } + + static inline UObject* GetSnowSetup() + { + auto Class = FindObject("/Game/Athena/Environments/Landscape/Blueprints/BP_SnowSetup.BP_SnowSetup_C"); + auto Actors = UGameplayStatics::GetAllActorsOfClass(GetWorld(), Class); + + return Actors.Num() > 0 ? Actors.at(0) : nullptr; + } + + static inline float GetFullSnowMapValue() + { + if (Fortnite_Version == 7.30) + { + std::vector> TimeAndValues = { { 0, 1.2f}, { 0.68104035f, 4.6893263f }, { 0.9632137f, 10.13335f }, { 1.0f, 15.0f } }; + // 1.2 + // 4.6893263 + // 10.13335 + // 15; + return TimeAndValues[3].first; + } + + return -1; + } + + static inline void SetSnow(float NewValue) + { + static auto SetSnowFn = FindObject("/Game/Athena/Environments/Landscape/Blueprints/BP_SnowSetup.BP_SnowSetup_C.SetSnow"); + auto SnowSetup = GetSnowSetup(); + + LOG_INFO(LogDev, "SnowSetup: {}", SnowSetup->IsValidLowLevel() ? SnowSetup->GetFullName() : "BadRead"); + + if (SnowSetup) + { + static auto OnReady_347B1F4D45630C357605FCB417D749A3Fn = FindObject("/Game/Athena/Environments/Landscape/Blueprints/BP_SnowSetup.BP_SnowSetup_C.OnReady_347B1F4D45630C357605FCB417D749A3"); + auto GameState = GetWorld()->GetGameState(); + SnowSetup->ProcessEvent(OnReady_347B1F4D45630C357605FCB417D749A3Fn, &GameState); + + SnowSetup->ProcessEvent(SetSnowFn, &NewValue); + } + } +} \ No newline at end of file diff --git a/Project Reboot 3.0/commands.h b/Project Reboot 3.0/commands.h index 46bfc26..4e99036 100644 --- a/Project Reboot 3.0/commands.h +++ b/Project Reboot 3.0/commands.h @@ -4,6 +4,7 @@ #include "FortPlayerControllerAthena.h" #include "KismetSystemLibrary.h" #include "AthenaBarrierObjective.h" +#include "FortAthenaMutator_Barrier.h" bool IsOperator(APlayerState* PlayerState, AFortPlayerController* PlayerController) { @@ -478,6 +479,24 @@ void ServerCheatHook(AFortPlayerControllerAthena* PlayerController, FString Msg) Pawn->TeleportTo(FVector(X, Y, Z), Pawn->GetActorRotation()); SendMessageToConsole(PlayerController, L"Teleported!"); } + else if (Command == "test") + { + auto SpawnBigWall = [&](AFortAthenaMutator* Mutator) { + if (auto BarrierMutator = Cast(Mutator)) + { + auto BigBaseWallClass = BarrierMutator->GetBigBaseWallClass(); + + LOG_INFO(LogDev, "BigBaseWallClass: {}", BigBaseWallClass->IsValidLowLevel() ? BigBaseWallClass->GetFullName() : "BadRead"); + + if (BigBaseWallClass->IsValidLowLevel()) + { + BarrierMutator->GetBigBaseWall() = GetWorld()->SpawnActor(BigBaseWallClass, FVector(0, 0, 0)); + } + } + }; + + LoopMutators(SpawnBigWall); + } else { bSendHelpMessage = true; }; } else { bSendHelpMessage = true; }; diff --git a/Project Reboot 3.0/dllmain.cpp b/Project Reboot 3.0/dllmain.cpp index ca4c398..dbdaef4 100644 --- a/Project Reboot 3.0/dllmain.cpp +++ b/Project Reboot 3.0/dllmain.cpp @@ -482,10 +482,9 @@ DWORD WINAPI Main(LPVOID) // Hooking::MinHook::Hook(FortPlayerStateAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerStateAthena.ServerSetInAircraft"), // AFortPlayerStateAthena::ServerSetInAircraftHook, (PVOID*)&AFortPlayerStateAthena::ServerSetInAircraftOriginal, false, true); // We could use second method but eh - if (FortOctopusVehicleDefault) + if (false && FortOctopusVehicleDefault) // hooking broken on 19.10 i cant figure it out for the life of me { static auto ServerUpdateTowhookFn = FindObject("/Script/FortniteGame.FortOctopusVehicle.ServerUpdateTowhook"); - Hooking::MinHook::Hook(FortOctopusVehicleDefault, ServerUpdateTowhookFn, AFortOctopusVehicle::ServerUpdateTowhookHook, nullptr, false); } diff --git a/Project Reboot 3.0/finder.h b/Project Reboot 3.0/finder.h index cbad427..b70ec24 100644 --- a/Project Reboot 3.0/finder.h +++ b/Project Reboot 3.0/finder.h @@ -1313,7 +1313,7 @@ static inline uint64 FindPickTeam() else if (Engine_Version >= 427) // different start return Memcury::Scanner::FindPattern("48 89 5C 24 ? 88 54 24 10 55 56 57 41 54 41 55 41 56 41 57 48 8B EC 48 83 EC 70 4C 8B A1").Get(); - if (Fortnite_Version == 7.20) + if (Fortnite_Version == 7.20 || Fortnite_Version == 7.30) return Memcury::Scanner::FindPattern("89 54 24 10 53 56 41 54 41 55 41 56 48 81 EC").Get(); auto Addr = Memcury::Scanner::FindStringRef(L"PickTeam for [%s] used beacon value [%d]", false, 0, Engine_Version >= 427); // todo check if its just s18+ but this doesn't matter for now cuz we hardcode sig diff --git a/Project Reboot 3.0/gui.h b/Project Reboot 3.0/gui.h index abd2c71..75538af 100644 --- a/Project Reboot 3.0/gui.h +++ b/Project Reboot 3.0/gui.h @@ -428,9 +428,22 @@ void MainUI() AllFortBeacons.Free(); + Globals::bInitializedPlaylist = false; + Globals::bStartedListening = false; + Globals::bHitReadyToStartMatch = false; + bStartedBus = false; + AmountOfRestarts++; + LOG_INFO(LogDev, "Switching!"); - ((AGameMode*)GetWorld()->GetGameMode())->RestartGame(); - // UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), LevelA, nullptr); + + if (Fortnite_Version >= 3) // idk what ver + { + ((AGameMode*)GetWorld()->GetGameMode())->RestartGame(); + } + else + { + UKismetSystemLibrary::ExecuteConsoleCommand(GetWorld(), LevelA, nullptr); + } /* @@ -444,18 +457,13 @@ void MainUI() // UGameplayStatics::OpenLevel(GetWorld(), UKismetStringLibrary::Conv_StringToName(LevelA), true, FString()); LOG_INFO(LogGame, "Restarting!"); - Globals::bInitializedPlaylist = false; - Globals::bStartedListening = false; - Globals::bHitReadyToStartMatch = false; - bStartedBus = false; - AmountOfRestarts++; } else { LOG_ERROR(LogGame, "Restarting is not supported on chapter 2 and above!"); } } - + /* if (ImGui::Button("TEST")) { auto GameMode = (AFortGameMode*)GetWorld()->GetGameMode(); @@ -493,6 +501,7 @@ void MainUI() } } } + */ if (!bStartedBus) { @@ -805,13 +814,46 @@ void MainUI() if (ImGui::Button("Print Class VFT")) { - auto ClassToDump = FindObject(ClassNameToDump)->CreateDefaultObject(); + auto Class = FindObject(ClassNameToDump); - if (ClassToDump) + if (Class) { - LOG_INFO(LogDev, "{} VFT: 0x{:x}", ClassToDump->GetName(), __int64(ClassToDump->VFTable) - __int64(GetModuleHandleW(0))); + auto ClassToDump = Class->CreateDefaultObject(); + + if (ClassToDump) + { + LOG_INFO(LogDev, "{} VFT: 0x{:x}", ClassToDump->GetName(), __int64(ClassToDump->VFTable) - __int64(GetModuleHandleW(0))); + } } } + + /* + ImGui::Text(std::format("Amount of hooks {}", AllFunctionHooks.size()).c_str()); + + for (auto& FunctionHook : AllFunctionHooks) + { + if (ImGui::Button(std::format("{} {} (0x{:x})", (FunctionHook.IsHooked ? "Unhook" : "Hook"), FunctionHook.Name, (__int64(FunctionHook.Original) - __int64(GetModuleHandleW(0)))).c_str())) + { + if (FunctionHook.IsHooked) + { + if (!FunctionHook.VFT || FunctionHook.Index == -1) + { + Hooking::MinHook::Unhook(FunctionHook.Original); + } + else + { + VirtualSwap(FunctionHook.VFT, FunctionHook.Index, FunctionHook.Original); + } + } + else + { + Hooking::MinHook::Hook(FunctionHook.Original, FunctionHook.Detour, nullptr, FunctionHook.Name); + } + + FunctionHook.IsHooked = !FunctionHook.IsHooked; + } + } + */ } else if (Tab == SETTINGS_TAB) { diff --git a/Project Reboot 3.0/hooking.h b/Project Reboot 3.0/hooking.h index fdf1377..c52dfb8 100644 --- a/Project Reboot 3.0/hooking.h +++ b/Project Reboot 3.0/hooking.h @@ -6,6 +6,18 @@ #include "memcury.h" #include "Class.h" +struct FunctionHooks +{ + void* Original; + void* Detour; + bool IsHooked; + std::string Name; + int Index = -1; + void** VFT = nullptr; +}; + +static inline std::vector AllFunctionHooks; + inline __int64 GetFunctionIdxOrPtr2(UFunction* Function) { auto NativeAddr = __int64(Function->GetFunc()); @@ -131,7 +143,7 @@ inline __int64 GetFunctionIdxOrPtr(UFunction* Function, bool bBreakWhenHitRet = for (int i = 0; i < 2000; i++) { - // LOG_INFO(LogDev, "0x{:x}", *(uint8_t*)(NativeAddr + i)); + // LOG_INFO(LogDev, "0x{:x} {}", *(uint8_t*)(NativeAddr + i), bFoundValidate); if (Fortnite_Version >= 19) // We should NOT do this, instead, if we expect a validate and we don't find before C3, then search for 0x41 0xFF. { @@ -205,6 +217,8 @@ inline __int64 GetFunctionIdxOrPtr(UFunction* Function, bool bBreakWhenHitRet = __int64 functionAddy = 0; + // LOG_INFO(LogDev, "not virtgual"); + if (RetAddr) { // LOG_INFO(LogDev, "RetAddr 0x{:x}", RetAddr - __int64(GetModuleHandleW(0))); @@ -233,17 +247,26 @@ namespace Hooking { namespace MinHook { - static bool Hook(void* Addr, void* Detour, void** Original = nullptr) + static bool Hook(void* Addr, void* Detour, void** Original = nullptr, std::string OptionalName = "Undefined") { LOG_INFO(LogDev, "Hooking 0x{:x}", __int64(Addr) - __int64(GetModuleHandleW(0))); - auto ret1 = MH_CreateHook(Addr, Detour, Original); + void* Og; + auto ret1 = MH_CreateHook(Addr, Detour, &Og); auto ret2 = MH_EnableHook(Addr); - return ret1 == MH_OK && ret2 == MH_OK; + + if (Original) + *Original = Og; + + bool wasHookSuccessful = ret1 == MH_OK && ret2 == MH_OK; + + if (wasHookSuccessful) + AllFunctionHooks.push_back(FunctionHooks(Og, Detour, true, OptionalName)); + + return wasHookSuccessful; } static bool PatchCall(void* Addr, void* Detour/*, void** Original = nullptr*/) { - // int64_t delta = targetAddr - (instrAddr + 5); // *(int32_t*)(instrAddr + 1) = static_cast(delta); } @@ -256,9 +279,11 @@ namespace Hooking if (!Function) return false; + auto FunctionName = Function->GetName(); + if (!DefaultClass || !DefaultClass->VFTable) { - LOG_WARN(LogHook, "DefaultClass or the vtable for function {} is null! ({})", Function->GetName(), __int64(DefaultClass)); + LOG_WARN(LogHook, "DefaultClass or the vtable for function {} is null! ({})", FunctionName, __int64(DefaultClass)); return false; } @@ -266,7 +291,7 @@ namespace Hooking if (bHookExec) { - LOG_INFO(LogDev, "Hooking Exec {} at 0x{:x}", Function->GetName(), __int64(Exec) - __int64(GetModuleHandleW(0))); + LOG_INFO(LogDev, "Hooking Exec {} at 0x{:x}", FunctionName, __int64(Exec) - __int64(GetModuleHandleW(0))); if (Original) *Original = Exec; @@ -279,7 +304,7 @@ namespace Hooking if (AddrOrIdx == -1) { - LOG_ERROR(LogInit, "Failed to find anything for {}.", Function->GetName()); + LOG_ERROR(LogInit, "Failed to find anything for {}.", FunctionName); return false; } @@ -287,22 +312,30 @@ namespace Hooking { auto Idx = AddrOrIdx / 8; + AllFunctionHooks.push_back(FunctionHooks(DefaultClass->VFTable[Idx], Detour, true, FunctionName, Idx, DefaultClass->VFTable)); + if (Original) *Original = DefaultClass->VFTable[Idx]; - LOG_INFO(LogDev, "Hooking {} with Idx 0x{:x}", Function->GetName(), AddrOrIdx); + LOG_INFO(LogDev, "Hooking {} with Idx 0x{:x}", FunctionName, AddrOrIdx); VirtualSwap(DefaultClass->VFTable, Idx, Detour); return true; } - return Hook((PVOID)AddrOrIdx, Detour, Original); + return Hook((PVOID)AddrOrIdx, Detour, Original, FunctionName); } static bool Unhook(void* Addr) { return MH_DisableHook((PVOID)Addr) == MH_OK; } + + /* static bool Unhook(void** Addr, void* Original) // I got brain damaged + { + Unhook(Addr); + *Addr = Original; + } */ } } \ No newline at end of file