From 33433ea9de6e53358f4b4df0caf07cba0ce84e50 Mon Sep 17 00:00:00 2001 From: Milxnor Date: Sat, 20 May 2023 10:22:47 -0400 Subject: [PATCH] respawning on older versions fixed respawning on pre s6, fixed a crash for some s5 builds, work on ai a bit, start on blueprint decompiler --- .../BuildingItemWeaponUpgradeActor.h | 14 + Project Reboot 3.0/Class.h | 5 +- .../FortAthenaAIBotCharacterCustomization.h | 1 + .../FortAthenaAIBotCustomizationData.h | 13 + Project Reboot 3.0/FortBotNameSettings.h | 36 + Project Reboot 3.0/FortGameModeAthena.cpp | 17 +- Project Reboot 3.0/FortPlayerController.cpp | 13 +- .../FortPlayerControllerAthena.h | 12 +- Project Reboot 3.0/FortPlayerPawn.cpp | 39 +- Project Reboot 3.0/FortPlayerPawn.h | 2 +- Project Reboot 3.0/FortPlayerPawnAthena.h | 6 + Project Reboot 3.0/FortPlayerState.cpp | 33 + Project Reboot 3.0/FortPlayerState.h | 4 +- Project Reboot 3.0/FortPlayerStateAthena.h | 5 + Project Reboot 3.0/GameModeBase.cpp | 2 +- Project Reboot 3.0/NetDriver.cpp | 7 + Project Reboot 3.0/Object.cpp | 6 +- Project Reboot 3.0/PlayerController.cpp | 2 +- Project Reboot 3.0/Project Reboot 3.0.vcxproj | 6 + .../Project Reboot 3.0.vcxproj.filters | 30 + Project Reboot 3.0/ScriptDisassembler.cpp | 1686 +++++++++++++++++ Project Reboot 3.0/ScriptDisassembler.h | 50 + Project Reboot 3.0/addresses.cpp | 3 +- Project Reboot 3.0/addresses.h | 1 + Project Reboot 3.0/ai.h | 102 +- Project Reboot 3.0/bots.h | 8 + Project Reboot 3.0/commands.h | 38 + Project Reboot 3.0/die.h | 5 - Project Reboot 3.0/dllmain.cpp | 11 +- Project Reboot 3.0/inc.h | 2 + 30 files changed, 2082 insertions(+), 77 deletions(-) create mode 100644 Project Reboot 3.0/BuildingItemWeaponUpgradeActor.h create mode 100644 Project Reboot 3.0/FortAthenaAIBotCharacterCustomization.h create mode 100644 Project Reboot 3.0/FortAthenaAIBotCustomizationData.h create mode 100644 Project Reboot 3.0/FortBotNameSettings.h create mode 100644 Project Reboot 3.0/ScriptDisassembler.cpp create mode 100644 Project Reboot 3.0/ScriptDisassembler.h diff --git a/Project Reboot 3.0/BuildingItemWeaponUpgradeActor.h b/Project Reboot 3.0/BuildingItemWeaponUpgradeActor.h new file mode 100644 index 0000000..4bda5d7 --- /dev/null +++ b/Project Reboot 3.0/BuildingItemWeaponUpgradeActor.h @@ -0,0 +1,14 @@ +#pragma once + +#include "BuildingGameplayActor.h" + +class ABuildingItemWeaponUpgradeActor : public ABuildingGameplayActor // ABuildingItemCollectorActor +{ +public: + + static class UClass* StaticClass() + { + static UClass* Class = FindObject(L"/Script/FortniteGame.BuildingItemWeaponUpgradeActor"); + return Class; + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/Class.h b/Project Reboot 3.0/Class.h index 84301ac..e376f57 100644 --- a/Project Reboot 3.0/Class.h +++ b/Project Reboot 3.0/Class.h @@ -16,13 +16,16 @@ class UStruct : public UField { public: int GetPropertiesSize(); + + UStruct* GetSuperStruct() { return *(UStruct**)(__int64(this) + Offsets::SuperStruct); } // idk if this is in UStruct + + TArray GetScript() { return *(TArray*)(__int64(this) + Offsets::Script); } }; class UClass : public UStruct { public: UObject* CreateDefaultObject(); - UClass* GetSuperStruct() { return *(UClass**)(__int64(this) + Offsets::SuperStruct); } // idk if this is in UStruct }; class UFunction : public UStruct diff --git a/Project Reboot 3.0/FortAthenaAIBotCharacterCustomization.h b/Project Reboot 3.0/FortAthenaAIBotCharacterCustomization.h new file mode 100644 index 0000000..6f70f09 --- /dev/null +++ b/Project Reboot 3.0/FortAthenaAIBotCharacterCustomization.h @@ -0,0 +1 @@ +#pragma once diff --git a/Project Reboot 3.0/FortAthenaAIBotCustomizationData.h b/Project Reboot 3.0/FortAthenaAIBotCustomizationData.h new file mode 100644 index 0000000..002512c --- /dev/null +++ b/Project Reboot 3.0/FortAthenaAIBotCustomizationData.h @@ -0,0 +1,13 @@ +#pragma once + +#include "reboot.h" + +class UFortAthenaAIBotCustomizationData : public UObject // UPrimaryDataAsset +{ +public: + static UClass* StaticClass() + { + static auto Class = FindObject("/Script/FortniteGame.FortAthenaAIBotCustomizationData"); + return Class; + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/FortBotNameSettings.h b/Project Reboot 3.0/FortBotNameSettings.h new file mode 100644 index 0000000..54a4fff --- /dev/null +++ b/Project Reboot 3.0/FortBotNameSettings.h @@ -0,0 +1,36 @@ +#pragma once + +#include "reboot.h" + +#include "Text.h" + +enum class EBotNamingMode : uint8 // idk if this changes +{ + RealName = 0, + SkinName = 1, + Anonymous = 2, + Custom = 3, + EBotNamingMode_MAX = 4, +}; + +class UFortBotNameSettings : public UObject +{ +public: + EBotNamingMode& GetNamingMode() + { + static auto NamingModeOffset = GetOffset("NamingMode"); + return Get(NamingModeOffset); + } + + FText& GetOverrideName() + { + static auto OverrideNameOffset = GetOffset("OverrideName"); + return Get(OverrideNameOffset); + } + + bool ShouldAddPlayerIDSuffix() + { + static auto bAddPlayerIDSuffixOffset = GetOffset("bAddPlayerIDSuffix"); + return Get(bAddPlayerIDSuffixOffset); + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/FortGameModeAthena.cpp b/Project Reboot 3.0/FortGameModeAthena.cpp index 3f98f43..cda3355 100644 --- a/Project Reboot 3.0/FortGameModeAthena.cpp +++ b/Project Reboot 3.0/FortGameModeAthena.cpp @@ -976,17 +976,22 @@ int AFortGameModeAthena::Athena_PickTeamHook(AFortGameModeAthena* GameMode, uint CurrentTeamMembers++; + auto PlayerStateObjectItem = GetItemByIndex(PlayerState->InternalIndex); + TWeakObjectPtr WeakPlayerState{}; WeakPlayerState.ObjectIndex = PlayerState->InternalIndex; - WeakPlayerState.ObjectSerialNumber = GetItemByIndex(PlayerState->InternalIndex)->SerialNumber; + WeakPlayerState.ObjectSerialNumber = PlayerStateObjectItem ? PlayerStateObjectItem->SerialNumber : 0; - if (auto TeamsArrayContainer = GameState->GetTeamsArrayContainer()) + if (PlayerStateObjectItem) { - auto& TeamArray = TeamsArrayContainer->TeamsArray.at(NextTeamIndex); - LOG_INFO(LogDev, "TeamsArrayContainer->TeamsArray.Num(): {}", TeamsArrayContainer->TeamsArray.Num()); - LOG_INFO(LogDev, "TeamArray.Num(): {}", TeamArray.Num()); + if (auto TeamsArrayContainer = GameState->GetTeamsArrayContainer()) + { + auto& TeamArray = TeamsArrayContainer->TeamsArray.at(NextTeamIndex); + LOG_INFO(LogDev, "TeamsArrayContainer->TeamsArray.Num(): {}", TeamsArrayContainer->TeamsArray.Num()); + LOG_INFO(LogDev, "TeamArray.Num(): {}", TeamArray.Num()); - TeamArray.Add(WeakPlayerState); + TeamArray.Add(WeakPlayerState); + } } return NextTeamIndex; diff --git a/Project Reboot 3.0/FortPlayerController.cpp b/Project Reboot 3.0/FortPlayerController.cpp index bf0b069..a68060c 100644 --- a/Project Reboot 3.0/FortPlayerController.cpp +++ b/Project Reboot 3.0/FortPlayerController.cpp @@ -1206,9 +1206,6 @@ void AFortPlayerController::ClientOnPawnDiedHook(AFortPlayerController* PlayerCo if (!DeadPawn || !GameState || !DeadPlayerState) return ClientOnPawnDiedOriginal(PlayerController, DeathReport); - static auto DeathInfoStruct = FindObject(L"/Script/FortniteGame.DeathInfo"); - static auto DeathInfoStructSize = DeathInfoStruct->GetPropertiesSize(); - auto DeathLocation = DeadPawn->GetActorLocation(); static auto FallDamageEnumValue = 1; @@ -1217,8 +1214,8 @@ void AFortPlayerController::ClientOnPawnDiedHook(AFortPlayerController* PlayerCo if (Fortnite_Version > 1.8 || Fortnite_Version == 1.11) { - auto DeathInfo = (void*)(__int64(DeadPlayerState) + MemberOffsets::FortPlayerStateAthena::DeathInfo); // Alloc(DeathInfoStructSize); - RtlSecureZeroMemory(DeathInfo, DeathInfoStructSize); // TODO FREE THE DEATHTAGS + auto DeathInfo = DeadPlayerState->GetDeathInfo(); // Alloc(DeathInfoStructSize); + DeadPlayerState->ClearDeathInfo(); auto/*&*/ Tags = MemberOffsets::FortPlayerPawn::CorrectTags == 0 ? FGameplayTagContainer() : DeadPawn->Get(MemberOffsets::FortPlayerPawn::CorrectTags); @@ -1425,13 +1422,13 @@ void AFortPlayerController::ClientOnPawnDiedHook(AFortPlayerController* PlayerCo { if (DamageCauser->IsA(FortProjectileBaseClass)) { - LOG_INFO(LogDev, "From a projectile!"); + // LOG_INFO(LogDev, "From a projectile!"); auto Owner = Cast(DamageCauser->GetOwner()); KillerWeaponDef = Owner->IsValidLowLevel() ? Owner->GetWeaponData() : nullptr; // I just added the IsValidLowLevel check because what if the weapon destroys? } if (auto Weapon = Cast(DamageCauser)) { - LOG_INFO(LogDev, "From a weapon!"); + // LOG_INFO(LogDev, "From a weapon!"); KillerWeaponDef = Weapon->GetWeaponData(); } } @@ -1517,6 +1514,8 @@ void AFortPlayerController::ClientOnPawnDiedHook(AFortPlayerController* PlayerCo // AllPlayerBotsToTick.remov3lbah } + DeadPlayerState->EndDBNOAbilities(); + return ClientOnPawnDiedOriginal(PlayerController, DeathReport); } diff --git a/Project Reboot 3.0/FortPlayerControllerAthena.h b/Project Reboot 3.0/FortPlayerControllerAthena.h index 2cb0667..8de5415 100644 --- a/Project Reboot 3.0/FortPlayerControllerAthena.h +++ b/Project Reboot 3.0/FortPlayerControllerAthena.h @@ -71,6 +71,14 @@ static bool ApplyCID(AFortPlayerPawn* Pawn, UObject* CID, bool bUseServerChooseP if (!PlayerController) return false; + if (bUseServerChoosePart) + { + if (Pawn) + { + + } + } + /* auto PCCosmeticLoadout = PlayerController->GetCosmeticLoadout(); if (!PCCosmeticLoadout) @@ -197,9 +205,9 @@ public: } } - void ClientOnPawnRevived(AController* EventInstigator) + void ClientOnPawnRevived(AController* EventInstigator) // actually zone // idk what this actually does but i call it { - static auto ClientOnPawnRevivedFn = FindObject("/Script/FortniteGame.FortPlayerControllerZone.ClientOnPawnRevived"); + static auto ClientOnPawnRevivedFn = FindObject(L"/Script/FortniteGame.FortPlayerControllerZone.ClientOnPawnRevived"); this->ProcessEvent(ClientOnPawnRevivedFn, &EventInstigator); } diff --git a/Project Reboot 3.0/FortPlayerPawn.cpp b/Project Reboot 3.0/FortPlayerPawn.cpp index aeffacf..1b54ac8 100644 --- a/Project Reboot 3.0/FortPlayerPawn.cpp +++ b/Project Reboot 3.0/FortPlayerPawn.cpp @@ -3,6 +3,7 @@ #include "FortPlayerController.h" #include "FortGadgetItemDefinition.h" #include "FortPlayerControllerAthena.h" +#include "FortPlayerPawnAthena.h" FFortAthenaLoadout* AFortPlayerPawn::GetCosmeticLoadout() { @@ -69,35 +70,7 @@ void AFortPlayerPawn::ServerReviveFromDBNOHook(AFortPlayerPawn* Pawn, AControlle else */ - static auto GAB_AthenaDBNOClass = FindObject(L"/Game/Abilities/NPC/Generic/GAB_AthenaDBNO.Default__GAB_AthenaDBNO_C"); - - auto DBNOPawnASC = PlayerState->GetAbilitySystemComponent(); - - if (!DBNOPawnASC) - return; - - FGameplayAbilitySpec* DBNOSpec = nullptr; - - UObject* ClassToFind = GAB_AthenaDBNOClass->ClassPrivate; - - auto compareAbilities = [&DBNOSpec, &ClassToFind](FGameplayAbilitySpec* Spec) { - auto CurrentAbility = Spec->GetAbility(); - - if (CurrentAbility->ClassPrivate == ClassToFind) - { - DBNOSpec = Spec; - return; - } - }; - - LoopSpecs(DBNOPawnASC, compareAbilities); - - if (!DBNOSpec) - return; - - DBNOPawnASC->ClientCancelAbility(DBNOSpec->GetHandle(), DBNOSpec->GetActivationInfo()); - DBNOPawnASC->ClientEndAbility(DBNOSpec->GetHandle(), DBNOSpec->GetActivationInfo()); - DBNOPawnASC->ServerEndAbility(DBNOSpec->GetHandle(), DBNOSpec->GetActivationInfo(), nullptr); + PlayerState->EndDBNOAbilities(); Pawn->SetDBNO(false); Pawn->SetHasPlayedDying(false); @@ -108,6 +81,14 @@ void AFortPlayerPawn::ServerReviveFromDBNOHook(AFortPlayerPawn* Pawn, AControlle PlayerController->ClientOnPawnRevived(EventInstigator); // We should call the function that calls this. PlayerController->RespawnPlayerAfterDeath(false); // nooo + + if (auto PawnAthena = Cast(Pawn)) // im too lazy to make another hook for fortplayerpawnathena + { + if (!PawnAthena->IsDBNO()) + { + PawnAthena->GetDBNORevivalStacking() = 0; + } + } } void AFortPlayerPawn::ServerHandlePickupWithRequestedSwapHook(UObject* Context, FFrame* Stack, void* Ret) diff --git a/Project Reboot 3.0/FortPlayerPawn.h b/Project Reboot 3.0/FortPlayerPawn.h index 476b0d6..985c9f8 100644 --- a/Project Reboot 3.0/FortPlayerPawn.h +++ b/Project Reboot 3.0/FortPlayerPawn.h @@ -43,7 +43,7 @@ public: AFortAthenaVehicle* GetVehicle(); UFortWeaponItemDefinition* GetVehicleWeaponDefinition(AFortAthenaVehicle* Vehicle); void UnEquipVehicleWeaponDefinition(UFortWeaponItemDefinition* VehicleWeaponDefinition); - + static void ServerReviveFromDBNOHook(AFortPlayerPawn* Pawn, AController* EventInstigator); static void ServerHandlePickupWithRequestedSwapHook(UObject* Context, FFrame* Stack, void* Ret); // we could native hook this but idk static void StartGhostModeExitHook(UObject* Context, FFrame* Stack, void* Ret); // we could native hook this but eh diff --git a/Project Reboot 3.0/FortPlayerPawnAthena.h b/Project Reboot 3.0/FortPlayerPawnAthena.h index 37c591c..831ae43 100644 --- a/Project Reboot 3.0/FortPlayerPawnAthena.h +++ b/Project Reboot 3.0/FortPlayerPawnAthena.h @@ -7,5 +7,11 @@ class AFortPlayerPawnAthena : public AFortPlayerPawn public: static inline void (*OnCapsuleBeginOverlapOriginal)(UObject* Context, FFrame* Stack, void* Ret); + uint8& GetDBNORevivalStacking() + { + static auto DBNORevivalStackingOffset = GetOffset("DBNORevivalStacking"); + return Get(DBNORevivalStackingOffset); + } + static void OnCapsuleBeginOverlapHook(UObject* Context, FFrame* Stack, void* Ret); }; \ No newline at end of file diff --git a/Project Reboot 3.0/FortPlayerState.cpp b/Project Reboot 3.0/FortPlayerState.cpp index 22aa741..83cead1 100644 --- a/Project Reboot 3.0/FortPlayerState.cpp +++ b/Project Reboot 3.0/FortPlayerState.cpp @@ -1,5 +1,38 @@ #include "FortPlayerState.h" +void AFortPlayerState::EndDBNOAbilities() +{ + static auto GAB_AthenaDBNOClass = FindObject(L"/Game/Abilities/NPC/Generic/GAB_AthenaDBNO.Default__GAB_AthenaDBNO_C"); + + auto ASC = this->GetAbilitySystemComponent(); + + if (!ASC) + return; + + FGameplayAbilitySpec* DBNOSpec = nullptr; + + UObject* ClassToFind = GAB_AthenaDBNOClass->ClassPrivate; + + auto compareAbilities = [&DBNOSpec, &ClassToFind](FGameplayAbilitySpec* Spec) { + auto CurrentAbility = Spec->GetAbility(); + + if (CurrentAbility->ClassPrivate == ClassToFind) + { + DBNOSpec = Spec; + return; + } + }; + + LoopSpecs(ASC, compareAbilities); + + if (!DBNOSpec) + return; + + ASC->ClientCancelAbility(DBNOSpec->GetHandle(), DBNOSpec->GetActivationInfo()); + ASC->ClientEndAbility(DBNOSpec->GetHandle(), DBNOSpec->GetActivationInfo()); + ASC->ServerEndAbility(DBNOSpec->GetHandle(), DBNOSpec->GetActivationInfo(), nullptr); +} + bool AFortPlayerState::AreUniqueIDsIdentical(FUniqueNetIdRepl* A, FUniqueNetIdRepl* B) { return A->IsIdentical(B); diff --git a/Project Reboot 3.0/FortPlayerState.h b/Project Reboot 3.0/FortPlayerState.h index ffdac58..d6b872b 100644 --- a/Project Reboot 3.0/FortPlayerState.h +++ b/Project Reboot 3.0/FortPlayerState.h @@ -19,11 +19,13 @@ public: return this->Get(WorldPlayerIdOffset); } + void EndDBNOAbilities(); + static bool AreUniqueIDsIdentical(FUniqueNetIdRepl* A, FUniqueNetIdRepl* B); static UClass* StaticClass() { - static auto Class = FindObject("/Script/FortniteGame.FortPlayerState"); + static auto Class = FindObject(L"/Script/FortniteGame.FortPlayerState"); return Class; } }; \ No newline at end of file diff --git a/Project Reboot 3.0/FortPlayerStateAthena.h b/Project Reboot 3.0/FortPlayerStateAthena.h index 38189bb..60867d9 100644 --- a/Project Reboot 3.0/FortPlayerStateAthena.h +++ b/Project Reboot 3.0/FortPlayerStateAthena.h @@ -123,6 +123,11 @@ public: return GetPtr(MemberOffsets::FortPlayerStateAthena::DeathInfo); } + void ClearDeathInfo() + { + RtlSecureZeroMemory(GetDeathInfo(), FDeathInfo::GetStructSize()); // TODO FREE THE DEATHTAGS + } + static void ServerSetInAircraftHook(UObject* Context, FFrame& Stack, void* Ret); static UClass* StaticClass() diff --git a/Project Reboot 3.0/GameModeBase.cpp b/Project Reboot 3.0/GameModeBase.cpp index d110504..0f05f9b 100644 --- a/Project Reboot 3.0/GameModeBase.cpp +++ b/Project Reboot 3.0/GameModeBase.cpp @@ -148,7 +148,7 @@ APawn* AGameModeBase::SpawnDefaultPawnForHook(AGameModeBase* GameMode, AControll /* auto DeathInfo = (void*)(__int64(PlayerStateAthena) + MemberOffsets::FortPlayerStateAthena::DeathInfo); FVector DeathLocation = MemberOffsets::DeathInfo::DeathLocation != -1 ? *(FVector*)(__int64(DeathInfo) + MemberOffsets::DeathInfo::DeathLocation) : FVector(0, 0, 0); - bIsRespawning = !(DeathLocation == FVector(0, 0, 0)); // bro kms */ + bIsRespawning = !(DeathLocation.CompareVectors(FVector(0, 0, 0))); // bro kms */ auto ASC = PlayerStateAthena->GetAbilitySystemComponent(); auto GameState = ((AFortGameModeAthena*)GameMode)->GetGameStateAthena(); diff --git a/Project Reboot 3.0/NetDriver.cpp b/Project Reboot 3.0/NetDriver.cpp index 2096652..1a9b1f9 100644 --- a/Project Reboot 3.0/NetDriver.cpp +++ b/Project Reboot 3.0/NetDriver.cpp @@ -9,6 +9,8 @@ #include #include "Package.h"S #include "AssertionMacros.h" +#include "bots.h" +#include "gui.h" FNetworkObjectList& UNetDriver::GetNetworkObjectList() { @@ -24,6 +26,11 @@ void UNetDriver::RemoveNetworkActor(AActor* Actor) void UNetDriver::TickFlushHook(UNetDriver* NetDriver) { + /* if (bEnableBotTick) + { + Bots::Tick(); + } */ + if (Globals::bStartedListening) { static auto ReplicationDriverOffset = NetDriver->GetOffset("ReplicationDriver", false); diff --git a/Project Reboot 3.0/Object.cpp b/Project Reboot 3.0/Object.cpp index d7e359f..82d4a5f 100644 --- a/Project Reboot 3.0/Object.cpp +++ b/Project Reboot 3.0/Object.cpp @@ -157,14 +157,14 @@ UPackage* UObject::GetOutermost() const bool UObject::IsA(UStruct* otherClass) { - UClass* super = ClassPrivate; + UStruct* super = ClassPrivate; while (super) { if (otherClass == super) return true; - super = *(UClass**)(__int64(super) + Offsets::SuperStruct); + super = super->GetSuperStruct(); } return false; @@ -174,7 +174,7 @@ UFunction* UObject::FindFunction(const std::string& ShortFunctionName) { // We could also loop through children. - UClass* super = ClassPrivate; + UStruct* super = ClassPrivate; while (super) { diff --git a/Project Reboot 3.0/PlayerController.cpp b/Project Reboot 3.0/PlayerController.cpp index 7b6ff7b..8993917 100644 --- a/Project Reboot 3.0/PlayerController.cpp +++ b/Project Reboot 3.0/PlayerController.cpp @@ -5,7 +5,7 @@ void APlayerController::ServerChangeName(FString& S) { - static auto ServerChangeNameFn = FindObject("/Script/Engine.PlayerController.ServerChangeName"); + static auto ServerChangeNameFn = FindObject(L"/Script/Engine.PlayerController.ServerChangeName"); this->ProcessEvent(ServerChangeNameFn, &S); } diff --git a/Project Reboot 3.0/Project Reboot 3.0.vcxproj b/Project Reboot 3.0/Project Reboot 3.0.vcxproj index b57a1bc..71525a4 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj @@ -256,6 +256,7 @@ + @@ -289,6 +290,7 @@ + @@ -319,6 +321,8 @@ + + @@ -339,6 +343,7 @@ + @@ -442,6 +447,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 085b6fa..e5e89ef 100644 --- a/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters +++ b/Project Reboot 3.0/Project Reboot 3.0.vcxproj.filters @@ -289,6 +289,9 @@ FortniteGame\Source\FortniteGame\Private\Player + + Engine\Source\Developer\ScriptDisassembler\Private + @@ -913,6 +916,21 @@ Engine\Source\Runtime\AIModule\Classes\EnvironmentQuery + + Engine\Source\Developer\ScriptDisassembler\Public + + + FortniteGame\Source\FortniteGame\Public\AI + + + FortniteGame\Source\FortniteGame\Public\AI + + + FortniteGame\Source\FortniteGame\Public\AI + + + FortniteGame\Source\FortniteGame\Public\Building\GameplayActors + @@ -1167,6 +1185,18 @@ {9b5483b5-3984-4fe1-96d2-e37d72642efd} + + {4c658de2-f9aa-4a42-9d7c-21014cb29b3d} + + + {f64d3dc0-39df-46a5-a451-2f9011ecbad1} + + + {1df3fb86-ba8f-446d-be0c-cfeb7ec94137} + + + {45f601de-1a88-490c-a74a-5cf729b16dfb} + diff --git a/Project Reboot 3.0/ScriptDisassembler.cpp b/Project Reboot 3.0/ScriptDisassembler.cpp new file mode 100644 index 0000000..14ac141 --- /dev/null +++ b/Project Reboot 3.0/ScriptDisassembler.cpp @@ -0,0 +1,1686 @@ +#include "ScriptDisassembler.h" + +enum EExprToken425 +{ + // Variable references. + EX_LocalVariable = 0x00, // A local variable. + EX_InstanceVariable = 0x01, // An object variable. + EX_DefaultVariable = 0x02, // Default variable for a class context. + // = 0x03, + EX_Return = 0x04, // Return from function. + // = 0x05, + EX_Jump = 0x06, // Goto a local address in code. + EX_JumpIfNot = 0x07, // Goto if not expression. + // = 0x08, + EX_Assert = 0x09, // Assertion. + // = 0x0A, + EX_Nothing = 0x0B, // No operation. + // = 0x0C, + // = 0x0D, + // = 0x0E, + EX_Let = 0x0F, // Assign an arbitrary size value to a variable. + // = 0x10, + // = 0x11, + EX_ClassContext = 0x12, // Class default object context. + EX_MetaCast = 0x13, // Metaclass cast. + EX_LetBool = 0x14, // Let boolean variable. + EX_EndParmValue = 0x15, // end of default value for optional function parameter + EX_EndFunctionParms = 0x16, // End of function call parameters. + EX_Self = 0x17, // Self object. + EX_Skip = 0x18, // Skippable expression. + EX_Context = 0x19, // Call a function through an object context. + EX_Context_FailSilent = 0x1A, // Call a function through an object context (can fail silently if the context is NULL; only generated for functions that don't have output or return values). + EX_VirtualFunction = 0x1B, // A function call with parameters. + EX_FinalFunction = 0x1C, // A prebound function call with parameters. + EX_IntConst = 0x1D, // Int constant. + EX_FloatConst = 0x1E, // Floating point constant. + EX_StringConst = 0x1F, // String constant. + EX_ObjectConst = 0x20, // An object constant. + EX_NameConst = 0x21, // A name constant. + EX_RotationConst = 0x22, // A rotation constant. + EX_VectorConst = 0x23, // A vector constant. + EX_ByteConst = 0x24, // A byte constant. + EX_IntZero = 0x25, // Zero. + EX_IntOne = 0x26, // One. + EX_True = 0x27, // Bool True. + EX_False = 0x28, // Bool False. + EX_TextConst = 0x29, // FText constant + EX_NoObject = 0x2A, // NoObject. + EX_TransformConst = 0x2B, // A transform constant + EX_IntConstByte = 0x2C, // Int constant that requires 1 byte. + EX_NoInterface = 0x2D, // A null interface (similar to EX_NoObject, but for interfaces) + EX_DynamicCast = 0x2E, // Safe dynamic class casting. + EX_StructConst = 0x2F, // An arbitrary UStruct constant + EX_EndStructConst = 0x30, // End of UStruct constant + EX_SetArray = 0x31, // Set the value of arbitrary array + EX_EndArray = 0x32, + // = 0x33, + EX_UnicodeStringConst = 0x34, // Unicode string constant. + EX_Int64Const = 0x35, // 64-bit integer constant. + EX_UInt64Const = 0x36, // 64-bit unsigned integer constant. + // = 0x37, + EX_PrimitiveCast = 0x38, // A casting operator for primitives which reads the type as the subsequent byte + EX_SetSet = 0x39, + EX_EndSet = 0x3A, + EX_SetMap = 0x3B, + EX_EndMap = 0x3C, + EX_SetConst = 0x3D, + EX_EndSetConst = 0x3E, + EX_MapConst = 0x3F, + EX_EndMapConst = 0x40, + // = 0x41, + EX_StructMemberContext = 0x42, // Context expression to address a property within a struct + EX_LetMulticastDelegate = 0x43, // Assignment to a multi-cast delegate + EX_LetDelegate = 0x44, // Assignment to a delegate + EX_LocalVirtualFunction = 0x45, // Special instructions to quickly call a virtual function that we know is going to run only locally + EX_LocalFinalFunction = 0x46, // Special instructions to quickly call a final function that we know is going to run only locally + // = 0x47, // CST_ObjectToBool + EX_LocalOutVariable = 0x48, // local out (pass by reference) function parameter + // = 0x49, // CST_InterfaceToBool + EX_DeprecatedOp4A = 0x4A, + EX_InstanceDelegate = 0x4B, // const reference to a delegate or normal function object + EX_PushExecutionFlow = 0x4C, // push an address on to the execution flow stack for future execution when a EX_PopExecutionFlow is executed. Execution continues on normally and doesn't change to the pushed address. + EX_PopExecutionFlow = 0x4D, // continue execution at the last address previously pushed onto the execution flow stack. + EX_ComputedJump = 0x4E, // Goto a local address in code, specified by an integer value. + EX_PopExecutionFlowIfNot = 0x4F, // continue execution at the last address previously pushed onto the execution flow stack, if the condition is not true. + EX_Breakpoint = 0x50, // Breakpoint. Only observed in the editor, otherwise it behaves like EX_Nothing. + EX_InterfaceContext = 0x51, // Call a function through a native interface variable + EX_ObjToInterfaceCast = 0x52, // Converting an object reference to native interface variable + EX_EndOfScript = 0x53, // Last byte in script code + EX_CrossInterfaceCast = 0x54, // Converting an interface variable reference to native interface variable + EX_InterfaceToObjCast = 0x55, // Converting an interface variable reference to an object + // = 0x56, + // = 0x57, + // = 0x58, + // = 0x59, + EX_WireTracepoint = 0x5A, // Trace point. Only observed in the editor, otherwise it behaves like EX_Nothing. + EX_SkipOffsetConst = 0x5B, // A CodeSizeSkipOffset constant + EX_AddMulticastDelegate = 0x5C, // Adds a delegate to a multicast delegate's targets + EX_ClearMulticastDelegate = 0x5D, // Clears all delegates in a multicast target + EX_Tracepoint = 0x5E, // Trace point. Only observed in the editor, otherwise it behaves like EX_Nothing. + EX_LetObj = 0x5F, // assign to any object ref pointer + EX_LetWeakObjPtr = 0x60, // assign to a weak object pointer + EX_BindDelegate = 0x61, // bind object and name to delegate + EX_RemoveMulticastDelegate = 0x62, // Remove a delegate from a multicast delegate's targets + EX_CallMulticastDelegate = 0x63, // Call multicast delegate + EX_LetValueOnPersistentFrame = 0x64, + EX_ArrayConst = 0x65, + EX_EndArrayConst = 0x66, + EX_SoftObjectConst = 0x67, + EX_CallMath = 0x68, // static pure function from on local call space + EX_SwitchValue = 0x69, + EX_InstrumentationEvent = 0x6A, // Instrumentation event + EX_ArrayGetByRef = 0x6B, + EX_ClassSparseDataVariable = 0x6C, // Sparse data variable + EX_FieldPathConst = 0x6D, + EX_Max = 0x100, +}; + + +void FKismetBytecodeDisassembler::DisassembleStructure(UFunction* Source) +{ + // Script.Empty(); + // Script.Append(Source->Script); + + for (int i = 0; i < Source->GetScript().Num(); i++) + { + Script.push_back(Source->GetScript().at(i)); + } + + int32 ScriptIndex = 0; + while (ScriptIndex < Script.size()) + { + Stream << std::format("Label_0x{:x}", ScriptIndex); + + AddIndent(); + SerializeExpr(ScriptIndex); + DropIndent(); + } +} + +uint8 FKismetBytecodeDisassembler::SerializeExpr(int32& ScriptIndex) +{ + AddIndent(); + + uint8 Opcode = Script[ScriptIndex]; + ScriptIndex++; + + ProcessCommon(ScriptIndex, Opcode); + + DropIndent(); + + return Opcode; +} + + +int32 FKismetBytecodeDisassembler::ReadINT(int32& ScriptIndex) +{ + int32 Value = Script[ScriptIndex]; ++ScriptIndex; + Value = Value | ((int32)Script[ScriptIndex] << 8); ++ScriptIndex; + Value = Value | ((int32)Script[ScriptIndex] << 16); ++ScriptIndex; + Value = Value | ((int32)Script[ScriptIndex] << 24); ++ScriptIndex; + + return Value; +} + +uint64 FKismetBytecodeDisassembler::ReadQWORD(int32& ScriptIndex) +{ + uint64 Value = Script[ScriptIndex]; ++ScriptIndex; + Value = Value | ((uint64)Script[ScriptIndex] << 8); ++ScriptIndex; + Value = Value | ((uint64)Script[ScriptIndex] << 16); ++ScriptIndex; + Value = Value | ((uint64)Script[ScriptIndex] << 24); ++ScriptIndex; + Value = Value | ((uint64)Script[ScriptIndex] << 32); ++ScriptIndex; + Value = Value | ((uint64)Script[ScriptIndex] << 40); ++ScriptIndex; + Value = Value | ((uint64)Script[ScriptIndex] << 48); ++ScriptIndex; + Value = Value | ((uint64)Script[ScriptIndex] << 56); ++ScriptIndex; + + return Value; +} + +struct FScriptName // todo mve +{ + uint32_t ComparisonIndex; + uint32_t DisplayIndex; + uint32_t Number = 0; +}; + +uint8 FKismetBytecodeDisassembler::ReadBYTE(int32& ScriptIndex) +{ + uint8 Value = Script[ScriptIndex]; ++ScriptIndex; + + return Value; +} + +std::string FKismetBytecodeDisassembler::ReadName(int32& ScriptIndex) +{ + const FScriptName ConstValue = *(FScriptName*)(Script.data() + ScriptIndex); + ScriptIndex += sizeof(FScriptName); + + FName Name; + Name.ComparisonIndex.Value = ConstValue.ComparisonIndex; + Name.Number = ConstValue.Number; + + return Name.ToString(); +} + +uint16 FKismetBytecodeDisassembler::ReadWORD(int32& ScriptIndex) +{ + uint16 Value = Script[ScriptIndex]; ++ScriptIndex; + Value = Value | ((uint16)Script[ScriptIndex] << 8); ++ScriptIndex; + return Value; +} + +float FKismetBytecodeDisassembler::ReadFLOAT(int32& ScriptIndex) +{ + union { float f; int32 i; } Result; + Result.i = ReadINT(ScriptIndex); + return Result.f; +} + +CodeSkipSizeType FKismetBytecodeDisassembler::ReadSkipCount(int32& ScriptIndex) +{ +#if SCRIPT_LIMIT_BYTECODE_TO_64KB + return ReadWORD(ScriptIndex); +#else + static_assert(sizeof(CodeSkipSizeType) == 4, "Update this code as size changed."); + return ReadINT(ScriptIndex); +#endif +} + +std::string FKismetBytecodeDisassembler::ReadString(int32& ScriptIndex) +{ + const uint8 Opcode = Script[ScriptIndex++]; + + switch (Opcode) + { + case EX_StringConst: + return ReadString8(ScriptIndex); + + case EX_UnicodeStringConst: + return ReadString16(ScriptIndex); + + default: + // checkf(false, TEXT("FKismetBytecodeDisassembler::ReadString - Unexpected opcode. Expected %d or %d, got %d"), (int)EX_StringConst, (int)EX_UnicodeStringConst, (int)Opcode); + break; + } + + return std::string(); +} + +std::string FKismetBytecodeDisassembler::ReadString8(int32& ScriptIndex) +{ + std::string Result; + + do + { + Result += (ANSICHAR)ReadBYTE(ScriptIndex); + } while (Script[ScriptIndex - 1] != 0); + + return Result; +} + +std::string FKismetBytecodeDisassembler::ReadString16(int32& ScriptIndex) +{ + std::string Result; + + do + { + Result += (TCHAR)ReadWORD(ScriptIndex); + } while ((Script[ScriptIndex - 1] != 0) || (Script[ScriptIndex - 2] != 0)); + + // Inline combine any surrogate pairs in the data when loading into a UTF-32 string + // StringConv::InlineCombineSurrogates(Result); + + return Result; +} + +void FKismetBytecodeDisassembler::ProcessCastByte(int32 CastType, int32& ScriptIndex) +{ + // Expression of cast + SerializeExpr(ScriptIndex); +} + +namespace EExprToken +{ + static uint8 GetPrimitiveCast() + { + if (Engine_Version == 425) + return EExprToken425::EX_PrimitiveCast; + } + + static uint8 GetEndSet() + { + if (Engine_Version == 425) + return EExprToken425::EX_EndSet; + } + + static uint8 GetSetConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_SetConst; + } + + static uint8 GetEndSetConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_EndSetConst; + } + + static uint8 GetSetMap() + { + if (Engine_Version == 425) + return EExprToken425::EX_SetMap; + } + + static uint8 GetEndMap() + { + if (Engine_Version == 425) + return EExprToken425::EX_EndMap; + } + + static uint8 GetMapConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_MapConst; + } + + static uint8 GetEndMapConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_EndMapConst; + } + + static uint8 GetObjToInterfaceCast() + { + if (Engine_Version == 425) + return EExprToken425::EX_ObjToInterfaceCast; + } + + static uint8 GetCrossInterfaceCast() + { + if (Engine_Version == 425) + return EExprToken425::EX_CrossInterfaceCast; + } + + static uint8 GetInterfaceToObjCast() + { + if (Engine_Version == 425) + return EExprToken425::EX_InterfaceToObjCast; + } + + static uint8 GetLet() + { + if (Engine_Version == 425) + return EExprToken425::EX_Let; + } + + static uint8 GetSetSet() + { + if (Engine_Version == 425) + return EExprToken425::EX_SetSet; + } + + static uint8 GetLetObj() + { + if (Engine_Version == 425) + return EExprToken425::EX_LetObj; + } + + static uint8 GetLetWeakObjectPtr() + { + if (Engine_Version == 425) + return EExprToken425::EX_LetWeakObjPtr; + } + + static uint8 GetLetBool() + { + if (Engine_Version == 425) + return EExprToken425::EX_LetBool; + } + + static uint8 GetLetValueOnPersistentFrame() + { + if (Engine_Version == 425) + return EExprToken425::EX_LetValueOnPersistentFrame; + } + + static uint8 GetStructMemberContext() + { + if (Engine_Version == 425) + return EExprToken425::EX_StructMemberContext; + } + + static uint8 GetLetDelegate() + { + if (Engine_Version == 425) + return EExprToken425::EX_LetDelegate; + } + + static uint8 GetLocalVirtualFunction() + { + if (Engine_Version == 425) + return EExprToken425::EX_LocalVirtualFunction; + } + + static uint8 GetLocalFinalFunction() + { + if (Engine_Version == 425) + return EExprToken425::EX_LocalFinalFunction; + } + + static uint8 GetLetMulticastDelegate() + { + if (Engine_Version == 425) + return EExprToken425::EX_LetMulticastDelegate; + } + + static uint8 GetComputedJump() + { + if (Engine_Version == 425) + return EExprToken425::EX_ComputedJump; + } + + static uint8 GetJump() + { + if (Engine_Version == 425) + return EExprToken425::EX_Jump; + } + + static uint8 GetLocalVariable() + { + if (Engine_Version == 425) + return EExprToken425::EX_LocalVariable; + } + + static uint8 GetDefaultVariable() + { + if (Engine_Version == 425) + return EExprToken425::EX_DefaultVariable; + } + + static uint8 GetInstanceVariable() + { + if (Engine_Version == 425) + return EExprToken425::EX_InstanceVariable; + } + + static uint8 GetLocalOutVariable() + { + if (Engine_Version == 425) + return EExprToken425::EX_LocalOutVariable; + } + + static uint8 GetClassSparseDataVariable() + { + if (Engine_Version == 425) + return EExprToken425::EX_ClassSparseDataVariable; + } + + static uint8 GetInterfaceContext() + { + if (Engine_Version == 425) + return EExprToken425::EX_InterfaceContext; + } + + static uint8 GetDeprecatedOp4A() + { + if (Engine_Version == 425) + return EExprToken425::EX_DeprecatedOp4A; + } + + static uint8 GetNothing() + { + if (Engine_Version == 425) + return EExprToken425::EX_Nothing; + } + + static uint8 GetEndOfScript() + { + if (Engine_Version == 425) + return EExprToken425::EX_EndOfScript; + } + + static uint8 GetEndFunctionParms() + { + if (Engine_Version == 425) + return EExprToken425::EX_EndFunctionParms; + } + + static uint8 GetEndStructConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_EndStructConst; + } + + static uint8 GetEndArray() + { + if (Engine_Version == 425) + return EExprToken425::EX_EndArray; + } + + static uint8 GetEndArrayConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_EndArrayConst; + } + + static uint8 GetIntZero() + { + if (Engine_Version == 425) + return EExprToken425::EX_IntZero; + } + + static uint8 GetIntOne() + { + if (Engine_Version == 425) + return EExprToken425::EX_IntOne; + } + + static uint8 GetTrue() + { + if (Engine_Version == 425) + return EExprToken425::EX_True; + } + + static uint8 GetFalse() + { + if (Engine_Version == 425) + return EExprToken425::EX_False; + } + + static uint8 GetNoObject() + { + if (Engine_Version == 425) + return EExprToken425::EX_NoObject; + } + + static uint8 GetNoInterface() + { + if (Engine_Version == 425) + return EExprToken425::EX_NoInterface; + } + + static uint8 GetSelf() + { + if (Engine_Version == 425) + return EExprToken425::EX_Self; + } + + static uint8 GetEndParmValue() + { + if (Engine_Version == 425) + return EExprToken425::EX_EndParmValue; + } + + static uint8 GetReturn() + { + if (Engine_Version == 425) + return EExprToken425::EX_Return; + } + + static uint8 GetCallMath() + { + if (Engine_Version == 425) + return EExprToken425::EX_CallMath; + } + + static uint8 GetFinalFunction() + { + if (Engine_Version == 425) + return EExprToken425::EX_FinalFunction; + } + + static uint8 GetCallMulticastDelegate() + { + if (Engine_Version == 425) + return EExprToken425::EX_CallMulticastDelegate; + } + + static uint8 GetVirtaulFunction() + { + if (Engine_Version == 425) + return EExprToken425::EX_VirtualFunction; + } + + static uint8 GetClassContext() + { + if (Engine_Version == 425) + return EExprToken425::EX_ClassContext; + } + + static uint8 GetContext() + { + if (Engine_Version == 425) + return EExprToken425::EX_Context; + } + + static uint8 GetContext_FailSilent() + { + if (Engine_Version == 425) + return EExprToken425::EX_Context_FailSilent; + } + + static uint8 GetIntConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_IntConst; + } + + static uint8 GetSkipOffsetConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_SkipOffsetConst; + } + + static uint8 GetFloatConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_FloatConst; + } + + static uint8 GetStringConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_StringConst; + } + + static uint8 GetUnicodeStringConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_UnicodeStringConst; + } + + static uint8 GetTextConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_TextConst; + } + + static uint8 GetObjectConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_ObjectConst; + } + + static uint8 GetSoftObjectConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_SoftObjectConst; + } + + static uint8 GetFieldPathConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_FieldPathConst; + } + + static uint8 GetNameConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_NameConst; + } + + static uint8 GetRotationConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_RotationConst; + } + + static uint8 GetVectorConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_VectorConst; + } + + static uint8 GetTransformConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_TransformConst; + } + + static uint8 GetStructConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_StructConst; + } + + static uint8 GetSetArray() + { + if (Engine_Version == 425) + return EExprToken425::EX_SetArray; + } + + static uint8 GetArrayConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_ArrayConst; + } + + static uint8 GetByteConst() + { + if (Engine_Version == 425) + return EExprToken425::EX_ByteConst; + } + + static uint8 GetIntConstByte() + { + if (Engine_Version == 425) + return EExprToken425::EX_IntConstByte; + } + + static uint8 GetMetaCast() + { + if (Engine_Version == 425) + return EExprToken425::EX_MetaCast; + } + + static uint8 GetDynamicCast() + { + if (Engine_Version == 425) + return EExprToken425::EX_DynamicCast; + } + + static uint8 GetJumpIfNot() + { + if (Engine_Version == 425) + return EExprToken425::EX_JumpIfNot; + } + + static uint8 GetAssert() + { + if (Engine_Version == 425) + return EExprToken425::EX_Assert; + } + + static uint8 GetSkip() + { + if (Engine_Version == 425) + return EExprToken425::EX_Skip; + } + + static uint8 GetInstanceDelegate() + { + if (Engine_Version == 425) + return EExprToken425::EX_InstanceDelegate; + } + + static uint8 GetAddMulticastDelegate() + { + if (Engine_Version == 425) + return EExprToken425::EX_AddMulticastDelegate; + } + + static uint8 GetRemoveMulticastDelegate() + { + if (Engine_Version == 425) + return EExprToken425::EX_RemoveMulticastDelegate; + } + + static uint8 GetClearMulticastDelegate() + { + if (Engine_Version == 425) + return EExprToken425::EX_ClearMulticastDelegate; + } + + static uint8 GetBindDelegate() + { + if (Engine_Version == 425) + return EExprToken425::EX_BindDelegate; + } + + static uint8 GetPushExecutionFlow() + { + if (Engine_Version == 425) + return EExprToken425::EX_PushExecutionFlow; + } + + static uint8 GetPopExecutionFlowIfNot() + { + if (Engine_Version == 425) + return EExprToken425::EX_PopExecutionFlowIfNot; + } + + static uint8 GetBreakpoint() + { + if (Engine_Version == 425) + return EExprToken425::EX_Breakpoint; + } + + static uint8 GetWireTracepoint() + { + if (Engine_Version == 425) + return EExprToken425::EX_WireTracepoint; + } + + static uint8 GetInstrumentationEvent() + { + if (Engine_Version == 425) + return EExprToken425::EX_InstrumentationEvent; + } + + static uint8 GetTracepoint() + { + if (Engine_Version == 425) + return EExprToken425::EX_Tracepoint; + } + + static uint8 GetSwitchValue() + { + if (Engine_Version == 425) + return EExprToken425::EX_SwitchValue; + } + + static uint8 GetArrayGetByRef() + { + if (Engine_Version == 425) + return EExprToken425::EX_ArrayGetByRef; + } +} + +std::string GetNameSafe(void* Property) +{ + FName* NamePrivate = nullptr; + + if (Engine_Version >= 425) + NamePrivate = (FName*)(__int64(Property) + 0x28); + else + NamePrivate = &((UField*)Property)->NamePrivate; + + return NamePrivate->ToString(); +} + +void FKismetBytecodeDisassembler::ProcessCommon(int32& ScriptIndex, uint8 Opcode) +{ + using FProperty = void; + + if (Opcode == EExprToken::GetPrimitiveCast()) + { + // A type conversion. + uint8 ConversionType = ReadBYTE(ScriptIndex); + Stream << std::format("{} PrimitiveCast of type {}", Indents, ConversionType); + AddIndent(); + + Stream << std::format("{} Argument:", Indents); + ProcessCastByte(ConversionType, ScriptIndex); + + //@TODO: + //Ar.Logf(TEXT("%s Expression:"), *Indents); + //SerializeExpr( ScriptIndex ); + } + else if (Opcode == EExprToken::GetSetSet()) + { + Stream << std::format("{} set set", Indents); + SerializeExpr(ScriptIndex); + ReadINT(ScriptIndex); + while (SerializeExpr(ScriptIndex) != EX_EndSet) + { + // Set contents + } + } + else if (Opcode == EExprToken::GetEndSet()) + { + Stream << std::format("{} EX_EndSet", Indents); + } + else if (Opcode == EExprToken::GetSetConst()) + { + FProperty* InnerProp = ReadPointer(ScriptIndex); + int32 Num = ReadINT(ScriptIndex); + Stream << std::format("{} set set const - elements number: {}, inner property: {}", Indents, Num, GetNameSafe(InnerProp)); + while (SerializeExpr(ScriptIndex) != EX_EndSetConst) + { + // Set contents + } + } + else if (Opcode == EExprToken::GetEndSetConst()) + { + Stream << std::format("{} EX_EndSetConst", Indents); + } + else if (Opcode == EExprToken::GetSetMap()) + { + Stream << std::format("{} set map", Indents); + SerializeExpr(ScriptIndex); + ReadINT(ScriptIndex); + while (SerializeExpr(ScriptIndex) != EX_EndMap) + { + // Map contents + } + } + else if (Opcode == EExprToken::GetEndMap()) + { + Stream << std::format("{} EX_EndMap", Indents); + } + else if (Opcode == EExprToken::GetMapConst()) + { + FProperty* KeyProp = ReadPointer(ScriptIndex); + FProperty* ValProp = ReadPointer(ScriptIndex); + int32 Num = ReadINT(ScriptIndex); + Stream << std::format("{} set map const - elements number: {}, key property: {}, val property: {}", Indents, Num, GetNameSafe(KeyProp), GetNameSafe(ValProp)); + while (SerializeExpr(ScriptIndex) != EX_EndMapConst) + { + // Map contents + } + } + else if (Opcode == EExprToken::GetEndMapConst()) + { + Stream << std::format("{} EX_EndMapConst", Indents); + } + else if (Opcode == EExprToken::GetObjToInterfaceCast()) + { + // A conversion from an object variable to a native interface variable. + // We use a different bytecode to avoid the branching each time we process a cast token + + // the interface class to convert to + UClass* InterfaceClass = ReadPointer(ScriptIndex); + Stream << std::format("{} ObjToInterfaceCast to {}", Indents, InterfaceClass->GetName()); + + SerializeExpr(ScriptIndex); + } + else if (Opcode == EExprToken::GetCrossInterfaceCast()) + { + // A conversion from one interface variable to a different interface variable. + // We use a different bytecode to avoid the branching each time we process a cast token + + // the interface class to convert to + UClass* InterfaceClass = ReadPointer(ScriptIndex); + Stream << std::format("{} InterfaceToInterfaceCast to {}", Indents, InterfaceClass->GetName()); + + SerializeExpr(ScriptIndex); + } + else if (Opcode == EExprToken::GetInterfaceToObjCast()) + { + // A conversion from an interface variable to a object variable. + // We use a different bytecode to avoid the branching each time we process a cast token + + // the interface class to convert to + UClass* ObjectClass = ReadPointer(ScriptIndex); + Stream << std::format("{} InterfaceToObjCast to {}", Indents, ObjectClass->GetName()); + + SerializeExpr(ScriptIndex); + } + else if (Opcode == EExprToken::GetLet()) + { + Stream << std::format("{} Let (Variable = Expression)", Indents); + AddIndent(); + + ReadPointer(ScriptIndex); + + // Variable expr. + Stream << std::format("{} Variable:", Indents); + SerializeExpr(ScriptIndex); + + // Assignment expr. + Stream << std::format("{} Expression:", Indents); + SerializeExpr(ScriptIndex); + + DropIndent(); + } + else if (Opcode == EExprToken::GetLetObj() || Opcode == EExprToken::GetLetWeakObjectPtr()) + { + if (Opcode == EExprToken::GetLetObj()) + { + Stream << std::format("{} Let Obj (Variable = Expression):", Indents); + } + else + { + Stream << std::format("{} Let WeakObjPtr (Variable = Expression):", Indents); + } + + AddIndent(); + + // Variable expr. + Stream << std::format("{} Variable:", Indents); + SerializeExpr(ScriptIndex); + + // Assignment expr. + Stream << std::format("{} Expression:", Indents); + SerializeExpr(ScriptIndex); + + DropIndent(); + } + else if (Opcode == EExprToken::GetLetBool()) + { + Stream << std::format("{} LetBool (Variable = Expression):", Indents); + AddIndent(); + + // Variable expr. + Stream << std::format("{} Variable:", Indents); + SerializeExpr(ScriptIndex); + + // Assignment expr. + Stream << std::format("{} Expression:", Indents); + SerializeExpr(ScriptIndex); + + DropIndent(); + } + /* + case EX_LetValueOnPersistentFrame: + { + Ar.Logf(TEXT("%s $%X: LetValueOnPersistentFrame"), *Indents, (int32)Opcode); + AddIndent(); + + auto Prop = ReadPointer(ScriptIndex); + Ar.Logf(TEXT("%s Destination variable: %s, offset: %d"), *Indents, *GetNameSafe(Prop), + Prop ? Prop->GetOffset_ForDebug() : 0); + + Ar.Logf(TEXT("%s Expression:"), *Indents); + SerializeExpr(ScriptIndex); + + DropIndent(); + + break; + } + case EX_StructMemberContext: + { + Ar.Logf(TEXT("%s $%X: Struct member context "), *Indents, (int32)Opcode); + AddIndent(); + + FProperty* Prop = ReadPointer(ScriptIndex); + + Ar.Logf(TEXT("%s Expression within struct %s, offset %d"), *Indents, *(Prop->GetName()), + Prop->GetOffset_ForDebug()); // although that isn't a UFunction, we are not going to indirect the props of a struct, so this should be fine + + Ar.Logf(TEXT("%s Expression to struct:"), *Indents); + SerializeExpr(ScriptIndex); + + DropIndent(); + + break; + } + case EX_LetDelegate: + { + Ar.Logf(TEXT("%s $%X: LetDelegate (Variable = Expression)"), *Indents, (int32)Opcode); + AddIndent(); + + // Variable expr. + Ar.Logf(TEXT("%s Variable:"), *Indents); + SerializeExpr(ScriptIndex); + + // Assignment expr. + Ar.Logf(TEXT("%s Expression:"), *Indents); + SerializeExpr(ScriptIndex); + + DropIndent(); + break; + } + case EX_LocalVirtualFunction: + { + FString FunctionName = ReadName(ScriptIndex); + Ar.Logf(TEXT("%s $%X: Local Virtual Script Function named %s"), *Indents, (int32)Opcode, *FunctionName); + + while (SerializeExpr(ScriptIndex) != EX_EndFunctionParms) + { + } + break; + } + case EX_LocalFinalFunction: + { + UStruct* StackNode = ReadPointer(ScriptIndex); + Ar.Logf(TEXT("%s $%X: Local Final Script Function (stack node %s::%s)"), *Indents, (int32)Opcode, StackNode ? *StackNode->GetOuter()->GetName() : TEXT("(null)"), StackNode ? *StackNode->GetName() : TEXT("(null)")); + + while (SerializeExpr(ScriptIndex) != EX_EndFunctionParms) + { + // Params + } + break; + } + case EX_LetMulticastDelegate: + { + Ar.Logf(TEXT("%s $%X: LetMulticastDelegate (Variable = Expression)"), *Indents, (int32)Opcode); + AddIndent(); + + // Variable expr. + Ar.Logf(TEXT("%s Variable:"), *Indents); + SerializeExpr(ScriptIndex); + + // Assignment expr. + Ar.Logf(TEXT("%s Expression:"), *Indents); + SerializeExpr(ScriptIndex); + + DropIndent(); + break; + } + + case EX_ComputedJump: + { + Ar.Logf(TEXT("%s $%X: Computed Jump, offset specified by expression:"), *Indents, (int32)Opcode); + + AddIndent(); + SerializeExpr(ScriptIndex); + DropIndent(); + + break; + } + + case EX_Jump: + { + CodeSkipSizeType SkipCount = ReadSkipCount(ScriptIndex); + Ar.Logf(TEXT("%s $%X: Jump to offset 0x%X"), *Indents, (int32)Opcode, SkipCount); + break; + } + case EX_LocalVariable: + { + FProperty* PropertyPtr = ReadPointer(ScriptIndex); + Ar.Logf(TEXT("%s $%X: Local variable named %s"), *Indents, (int32)Opcode, PropertyPtr ? *PropertyPtr->GetName() : TEXT("(null)")); + break; + } + case EX_DefaultVariable: + { + FProperty* PropertyPtr = ReadPointer(ScriptIndex); + Ar.Logf(TEXT("%s $%X: Default variable named %s"), *Indents, (int32)Opcode, PropertyPtr ? *PropertyPtr->GetName() : TEXT("(null)")); + break; + } + case EX_InstanceVariable: + { + FProperty* PropertyPtr = ReadPointer(ScriptIndex); + Ar.Logf(TEXT("%s $%X: Instance variable named %s"), *Indents, (int32)Opcode, PropertyPtr ? *PropertyPtr->GetName() : TEXT("(null)")); + break; + } + case EX_LocalOutVariable: + { + FProperty* PropertyPtr = ReadPointer(ScriptIndex); + Ar.Logf(TEXT("%s $%X: Local out variable named %s"), *Indents, (int32)Opcode, PropertyPtr ? *PropertyPtr->GetName() : TEXT("(null)")); + break; + } + case EX_ClassSparseDataVariable: + { + FProperty* PropertyPtr = ReadPointer(ScriptIndex); + Ar.Logf(TEXT("%s $%X: Class sparse data variable named %s"), *Indents, (int32)Opcode, PropertyPtr ? *PropertyPtr->GetName() : TEXT("(null)")); + break; + } + case EX_InterfaceContext: + { + Ar.Logf(TEXT("%s $%X: EX_InterfaceContext:"), *Indents, (int32)Opcode); + SerializeExpr(ScriptIndex); + break; + } + case EX_DeprecatedOp4A: + { + Ar.Logf(TEXT("%s $%X: This opcode has been removed and does nothing."), *Indents, (int32)Opcode); + break; + } + case EX_Nothing: + { + Ar.Logf(TEXT("%s $%X: EX_Nothing"), *Indents, (int32)Opcode); + break; + } + case EX_EndOfScript: + { + Ar.Logf(TEXT("%s $%X: EX_EndOfScript"), *Indents, (int32)Opcode); + break; + } + case EX_EndFunctionParms: + { + Ar.Logf(TEXT("%s $%X: EX_EndFunctionParms"), *Indents, (int32)Opcode); + break; + } + case EX_EndStructConst: + { + Ar.Logf(TEXT("%s $%X: EX_EndStructConst"), *Indents, (int32)Opcode); + break; + } + case EX_EndArray: + { + Ar.Logf(TEXT("%s $%X: EX_EndArray"), *Indents, (int32)Opcode); + break; + } + case EX_EndArrayConst: + { + Ar.Logf(TEXT("%s $%X: EX_EndArrayConst"), *Indents, (int32)Opcode); + break; + } + case EX_IntZero: + { + Ar.Logf(TEXT("%s $%X: EX_IntZero"), *Indents, (int32)Opcode); + break; + } + case EX_IntOne: + { + Ar.Logf(TEXT("%s $%X: EX_IntOne"), *Indents, (int32)Opcode); + break; + } + case EX_True: + { + Ar.Logf(TEXT("%s $%X: EX_True"), *Indents, (int32)Opcode); + break; + } + case EX_False: + { + Ar.Logf(TEXT("%s $%X: EX_False"), *Indents, (int32)Opcode); + break; + } + case EX_NoObject: + { + Ar.Logf(TEXT("%s $%X: EX_NoObject"), *Indents, (int32)Opcode); + break; + } + case EX_NoInterface: + { + Ar.Logf(TEXT("%s $%X: EX_NoObject"), *Indents, (int32)Opcode); + break; + } + case EX_Self: + { + Ar.Logf(TEXT("%s $%X: EX_Self"), *Indents, (int32)Opcode); + break; + } + case EX_EndParmValue: + { + Ar.Logf(TEXT("%s $%X: EX_EndParmValue"), *Indents, (int32)Opcode); + break; + } + case EX_Return: + { + Ar.Logf(TEXT("%s $%X: Return expression"), *Indents, (int32)Opcode); + + SerializeExpr(ScriptIndex); // Return expression. + break; + } + case EX_CallMath: + { + UStruct* StackNode = ReadPointer(ScriptIndex); + Ar.Logf(TEXT("%s $%X: Call Math (stack node %s::%s)"), *Indents, (int32)Opcode, *GetNameSafe(StackNode ? StackNode->GetOuter() : nullptr), *GetNameSafe(StackNode)); + + while (SerializeExpr(ScriptIndex) != EX_EndFunctionParms) + { + // Params + } + break; + } + case EX_FinalFunction: + { + UStruct* StackNode = ReadPointer(ScriptIndex); + Ar.Logf(TEXT("%s $%X: Final Function (stack node %s::%s)"), *Indents, (int32)Opcode, StackNode ? *StackNode->GetOuter()->GetName() : TEXT("(null)"), StackNode ? *StackNode->GetName() : TEXT("(null)")); + + while (SerializeExpr(ScriptIndex) != EX_EndFunctionParms) + { + // Params + } + break; + } + case EX_CallMulticastDelegate: + { + UStruct* StackNode = ReadPointer(ScriptIndex); + Ar.Logf(TEXT("%s $%X: CallMulticastDelegate (signature %s::%s) delegate:"), *Indents, (int32)Opcode, StackNode ? *StackNode->GetOuter()->GetName() : TEXT("(null)"), StackNode ? *StackNode->GetName() : TEXT("(null)")); + SerializeExpr(ScriptIndex); + Ar.Logf(TEXT("Params:")); + while (SerializeExpr(ScriptIndex) != EX_EndFunctionParms) + { + // Params + } + break; + } + case EX_VirtualFunction: + { + FString FunctionName = ReadName(ScriptIndex); + Ar.Logf(TEXT("%s $%X: Virtual Function named %s"), *Indents, (int32)Opcode, *FunctionName); + + while (SerializeExpr(ScriptIndex) != EX_EndFunctionParms) + { + } + break; + } + case EX_ClassContext: + case EX_Context: + case EX_Context_FailSilent: + { + Ar.Logf(TEXT("%s $%X: %s"), *Indents, (int32)Opcode, Opcode == EX_ClassContext ? TEXT("Class Context") : TEXT("Context")); + AddIndent(); + + // Object expression. + Ar.Logf(TEXT("%s ObjectExpression:"), *Indents); + SerializeExpr(ScriptIndex); + + if (Opcode == EX_Context_FailSilent) + { + Ar.Logf(TEXT(" Can fail silently on access none ")); + } + + // Code offset for NULL expressions. + CodeSkipSizeType SkipCount = ReadSkipCount(ScriptIndex); + Ar.Logf(TEXT("%s Skip Bytes: 0x%X"), *Indents, SkipCount); + + // Property corresponding to the r-value data, in case the l-value needs to be mem-zero'd + FField* Field = ReadPointer(ScriptIndex); + Ar.Logf(TEXT("%s R-Value Property: %s"), *Indents, Field ? *Field->GetName() : TEXT("(null)")); + + // Context expression. + Ar.Logf(TEXT("%s ContextExpression:"), *Indents); + SerializeExpr(ScriptIndex); + + DropIndent(); + break; + } + case EX_IntConst: + { + int32 ConstValue = ReadINT(ScriptIndex); + Ar.Logf(TEXT("%s $%X: literal int32 %d"), *Indents, (int32)Opcode, ConstValue); + break; + } + case EX_SkipOffsetConst: + { + CodeSkipSizeType ConstValue = ReadSkipCount(ScriptIndex); + Ar.Logf(TEXT("%s $%X: literal CodeSkipSizeType 0x%X"), *Indents, (int32)Opcode, ConstValue); + break; + } + case EX_FloatConst: + { + float ConstValue = ReadFLOAT(ScriptIndex); + Ar.Logf(TEXT("%s $%X: literal float %f"), *Indents, (int32)Opcode, ConstValue); + break; + } + case EX_StringConst: + { + FString ConstValue = ReadString8(ScriptIndex); + Ar.Logf(TEXT("%s $%X: literal ansi string \"%s\""), *Indents, (int32)Opcode, *ConstValue); + break; + } + case EX_UnicodeStringConst: + { + FString ConstValue = ReadString16(ScriptIndex); + Ar.Logf(TEXT("%s $%X: literal unicode string \"%s\""), *Indents, (int32)Opcode, *ConstValue); + break; + } + case EX_TextConst: + { + // What kind of text are we dealing with? + const EBlueprintTextLiteralType TextLiteralType = (EBlueprintTextLiteralType)Script[ScriptIndex++]; + + switch (TextLiteralType) + { + case EBlueprintTextLiteralType::Empty: + { + Ar.Logf(TEXT("%s $%X: literal text - empty"), *Indents, (int32)Opcode); + } + break; + + case EBlueprintTextLiteralType::LocalizedText: + { + const FString SourceString = ReadString(ScriptIndex); + const FString KeyString = ReadString(ScriptIndex); + const FString Namespace = ReadString(ScriptIndex); + Ar.Logf(TEXT("%s $%X: literal text - localized text { namespace: \"%s\", key: \"%s\", source: \"%s\" }"), *Indents, (int32)Opcode, *Namespace, *KeyString, *SourceString); + } + break; + + case EBlueprintTextLiteralType::InvariantText: + { + const FString SourceString = ReadString(ScriptIndex); + Ar.Logf(TEXT("%s $%X: literal text - invariant text: \"%s\""), *Indents, (int32)Opcode, *SourceString); + } + break; + + case EBlueprintTextLiteralType::LiteralString: + { + const FString SourceString = ReadString(ScriptIndex); + Ar.Logf(TEXT("%s $%X: literal text - literal string: \"%s\""), *Indents, (int32)Opcode, *SourceString); + } + break; + + case EBlueprintTextLiteralType::StringTableEntry: + { + ReadPointer(ScriptIndex); // String Table asset (if any) + const FString TableIdString = ReadString(ScriptIndex); + const FString KeyString = ReadString(ScriptIndex); + Ar.Logf(TEXT("%s $%X: literal text - string table entry { tableid: \"%s\", key: \"%s\" }"), *Indents, (int32)Opcode, *TableIdString, *KeyString); + } + break; + + default: + checkf(false, TEXT("Unknown EBlueprintTextLiteralType! Please update FKismetBytecodeDisassembler::ProcessCommon to handle this type of text.")); + break; + } + break; + } + case EX_ObjectConst: + { + UObject* Pointer = ReadPointer(ScriptIndex); + Ar.Logf(TEXT("%s $%X: EX_ObjectConst (%p:%s)"), *Indents, (int32)Opcode, Pointer, *Pointer->GetFullName()); + break; + } + case EX_SoftObjectConst: + { + Ar.Logf(TEXT("%s $%X: EX_SoftObjectConst"), *Indents, (int32)Opcode); + SerializeExpr(ScriptIndex); + break; + } + case EX_FieldPathConst: + { + Ar.Logf(TEXT("%s $%X: EX_FieldPathConst"), *Indents, (int32)Opcode); + SerializeExpr(ScriptIndex); + break; + } + case EX_NameConst: + { + FString ConstValue = ReadName(ScriptIndex); + Ar.Logf(TEXT("%s $%X: literal name %s"), *Indents, (int32)Opcode, *ConstValue); + break; + } + case EX_RotationConst: + { + float Pitch = ReadFLOAT(ScriptIndex); + float Yaw = ReadFLOAT(ScriptIndex); + float Roll = ReadFLOAT(ScriptIndex); + + Ar.Logf(TEXT("%s $%X: literal rotation (%f,%f,%f)"), *Indents, (int32)Opcode, Pitch, Yaw, Roll); + break; + } + case EX_VectorConst: + { + float X = ReadFLOAT(ScriptIndex); + float Y = ReadFLOAT(ScriptIndex); + float Z = ReadFLOAT(ScriptIndex); + + Ar.Logf(TEXT("%s $%X: literal vector (%f,%f,%f)"), *Indents, (int32)Opcode, X, Y, Z); + break; + } + case EX_TransformConst: + { + + float RotX = ReadFLOAT(ScriptIndex); + float RotY = ReadFLOAT(ScriptIndex); + float RotZ = ReadFLOAT(ScriptIndex); + float RotW = ReadFLOAT(ScriptIndex); + + float TransX = ReadFLOAT(ScriptIndex); + float TransY = ReadFLOAT(ScriptIndex); + float TransZ = ReadFLOAT(ScriptIndex); + + float ScaleX = ReadFLOAT(ScriptIndex); + float ScaleY = ReadFLOAT(ScriptIndex); + float ScaleZ = ReadFLOAT(ScriptIndex); + + Ar.Logf(TEXT("%s $%X: literal transform R(%f,%f,%f,%f) T(%f,%f,%f) S(%f,%f,%f)"), *Indents, (int32)Opcode, TransX, TransY, TransZ, RotX, RotY, RotZ, RotW, ScaleX, ScaleY, ScaleZ); + break; + } + case EX_StructConst: + { + UScriptStruct* Struct = ReadPointer(ScriptIndex); + int32 SerializedSize = ReadINT(ScriptIndex); + Ar.Logf(TEXT("%s $%X: literal struct %s (serialized size: %d)"), *Indents, (int32)Opcode, *Struct->GetName(), SerializedSize); + while (SerializeExpr(ScriptIndex) != EX_EndStructConst) + { + // struct contents + } + break; + } + case EX_SetArray: + { + Ar.Logf(TEXT("%s $%X: set array"), *Indents, (int32)Opcode); + SerializeExpr(ScriptIndex); + while (SerializeExpr(ScriptIndex) != EX_EndArray) + { + // Array contents + } + break; + } + case EX_ArrayConst: + { + FProperty* InnerProp = ReadPointer(ScriptIndex); + int32 Num = ReadINT(ScriptIndex); + Ar.Logf(TEXT("%s $%X: set array const - elements number: %d, inner property: %s"), *Indents, (int32)Opcode, Num, *GetNameSafe(InnerProp)); + while (SerializeExpr(ScriptIndex) != EX_EndArrayConst) + { + // Array contents + } + break; + } + case EX_ByteConst: + { + uint8 ConstValue = ReadBYTE(ScriptIndex); + Ar.Logf(TEXT("%s $%X: literal byte %d"), *Indents, (int32)Opcode, ConstValue); + break; + } + case EX_IntConstByte: + { + int32 ConstValue = ReadBYTE(ScriptIndex); + Ar.Logf(TEXT("%s $%X: literal int %d"), *Indents, (int32)Opcode, ConstValue); + break; + } + case EX_MetaCast: + { + UClass* Class = ReadPointer(ScriptIndex); + Ar.Logf(TEXT("%s $%X: MetaCast to %s of expr:"), *Indents, (int32)Opcode, *Class->GetName()); + SerializeExpr(ScriptIndex); + break; + } + case EX_DynamicCast: + { + UClass* Class = ReadPointer(ScriptIndex); + Ar.Logf(TEXT("%s $%X: DynamicCast to %s of expr:"), *Indents, (int32)Opcode, *Class->GetName()); + SerializeExpr(ScriptIndex); + break; + } + case EX_JumpIfNot: + { + // Code offset. + CodeSkipSizeType SkipCount = ReadSkipCount(ScriptIndex); + + Ar.Logf(TEXT("%s $%X: Jump to offset 0x%X if not expr:"), *Indents, (int32)Opcode, SkipCount); + + // Boolean expr. + SerializeExpr(ScriptIndex); + break; + } + case EX_Assert: + { + uint16 LineNumber = ReadWORD(ScriptIndex); + uint8 InDebugMode = ReadBYTE(ScriptIndex); + + Ar.Logf(TEXT("%s $%X: assert at line %d, in debug mode = %d with expr:"), *Indents, (int32)Opcode, LineNumber, InDebugMode); + SerializeExpr(ScriptIndex); // Assert expr. + break; + } + case EX_Skip: + { + CodeSkipSizeType W = ReadSkipCount(ScriptIndex); + Ar.Logf(TEXT("%s $%X: possibly skip 0x%X bytes of expr:"), *Indents, (int32)Opcode, W); + + // Expression to possibly skip. + SerializeExpr(ScriptIndex); + + break; + } + case EX_InstanceDelegate: + { + // the name of the function assigned to the delegate. + FString FuncName = ReadName(ScriptIndex); + + Ar.Logf(TEXT("%s $%X: instance delegate function named %s"), *Indents, (int32)Opcode, *FuncName); + break; + } + case EX_AddMulticastDelegate: + { + Ar.Logf(TEXT("%s $%X: Add MC delegate"), *Indents, (int32)Opcode); + SerializeExpr(ScriptIndex); + SerializeExpr(ScriptIndex); + break; + } + case EX_RemoveMulticastDelegate: + { + Ar.Logf(TEXT("%s $%X: Remove MC delegate"), *Indents, (int32)Opcode); + SerializeExpr(ScriptIndex); + SerializeExpr(ScriptIndex); + break; + } + case EX_ClearMulticastDelegate: + { + Ar.Logf(TEXT("%s $%X: Clear MC delegate"), *Indents, (int32)Opcode); + SerializeExpr(ScriptIndex); + break; + } + case EX_BindDelegate: + { + // the name of the function assigned to the delegate. + FString FuncName = ReadName(ScriptIndex); + + Ar.Logf(TEXT("%s $%X: BindDelegate '%s' "), *Indents, (int32)Opcode, *FuncName); + + Ar.Logf(TEXT("%s Delegate:"), *Indents); + SerializeExpr(ScriptIndex); + + Ar.Logf(TEXT("%s Object:"), *Indents); + SerializeExpr(ScriptIndex); + + break; + } + case EX_PushExecutionFlow: + { + CodeSkipSizeType SkipCount = ReadSkipCount(ScriptIndex); + Ar.Logf(TEXT("%s $%X: FlowStack.Push(0x%X);"), *Indents, (int32)Opcode, SkipCount); + break; + } + case EX_PopExecutionFlow: + { + Ar.Logf(TEXT("%s $%X: if (FlowStack.Num()) { jump to statement at FlowStack.Pop(); } else { ERROR!!! }"), *Indents, (int32)Opcode); + break; + } + case EX_PopExecutionFlowIfNot: + { + Ar.Logf(TEXT("%s $%X: if (!condition) { if (FlowStack.Num()) { jump to statement at FlowStack.Pop(); } else { ERROR!!! } }"), *Indents, (int32)Opcode); + // Boolean expr. + SerializeExpr(ScriptIndex); + break; + } + case EX_Breakpoint: + { + Ar.Logf(TEXT("%s $%X: <<< BREAKPOINT >>>"), *Indents, (int32)Opcode); + break; + } + case EX_WireTracepoint: + { + Ar.Logf(TEXT("%s $%X: .. wire debug site .."), *Indents, (int32)Opcode); + break; + } + case EX_InstrumentationEvent: + { + const uint8 EventType = ReadBYTE(ScriptIndex); + switch (EventType) + { + case EScriptInstrumentation::InlineEvent: + Ar.Logf(TEXT("%s $%X: .. instrumented inline event .."), *Indents, (int32)Opcode); + break; + case EScriptInstrumentation::Stop: + Ar.Logf(TEXT("%s $%X: .. instrumented event stop .."), *Indents, (int32)Opcode); + break; + case EScriptInstrumentation::PureNodeEntry: + Ar.Logf(TEXT("%s $%X: .. instrumented pure node entry site .."), *Indents, (int32)Opcode); + break; + case EScriptInstrumentation::NodeDebugSite: + Ar.Logf(TEXT("%s $%X: .. instrumented debug site .."), *Indents, (int32)Opcode); + break; + case EScriptInstrumentation::NodeEntry: + Ar.Logf(TEXT("%s $%X: .. instrumented wire entry site .."), *Indents, (int32)Opcode); + break; + case EScriptInstrumentation::NodeExit: + Ar.Logf(TEXT("%s $%X: .. instrumented wire exit site .."), *Indents, (int32)Opcode); + break; + case EScriptInstrumentation::PushState: + Ar.Logf(TEXT("%s $%X: .. push execution state .."), *Indents, (int32)Opcode); + break; + case EScriptInstrumentation::RestoreState: + Ar.Logf(TEXT("%s $%X: .. restore execution state .."), *Indents, (int32)Opcode); + break; + case EScriptInstrumentation::ResetState: + Ar.Logf(TEXT("%s $%X: .. reset execution state .."), *Indents, (int32)Opcode); + break; + case EScriptInstrumentation::SuspendState: + Ar.Logf(TEXT("%s $%X: .. suspend execution state .."), *Indents, (int32)Opcode); + break; + case EScriptInstrumentation::PopState: + Ar.Logf(TEXT("%s $%X: .. pop execution state .."), *Indents, (int32)Opcode); + break; + case EScriptInstrumentation::TunnelEndOfThread: + Ar.Logf(TEXT("%s $%X: .. tunnel end of thread .."), *Indents, (int32)Opcode); + break; + } + break; + } + case EX_Tracepoint: + { + Ar.Logf(TEXT("%s $%X: .. debug site .."), *Indents, (int32)Opcode); + break; + } + case EX_SwitchValue: + { + const auto NumCases = ReadWORD(ScriptIndex); + const auto AfterSkip = ReadSkipCount(ScriptIndex); + + Ar.Logf(TEXT("%s $%X: Switch Value %d cases, end in 0x%X"), *Indents, (int32)Opcode, NumCases, AfterSkip); + AddIndent(); + Ar.Logf(TEXT("%s Index:"), *Indents); + SerializeExpr(ScriptIndex); + + for (uint16 CaseIndex = 0; CaseIndex < NumCases; ++CaseIndex) + { + Ar.Logf(TEXT("%s [%d] Case Index (label: 0x%X):"), *Indents, CaseIndex, ScriptIndex); + SerializeExpr(ScriptIndex); // case index value term + const auto OffsetToNextCase = ReadSkipCount(ScriptIndex); + Ar.Logf(TEXT("%s [%d] Offset to the next case: 0x%X"), *Indents, CaseIndex, OffsetToNextCase); + Ar.Logf(TEXT("%s [%d] Case Result:"), *Indents, CaseIndex); + SerializeExpr(ScriptIndex); // case term + } + + Ar.Logf(TEXT("%s Default result (label: 0x%X):"), *Indents, ScriptIndex); + SerializeExpr(ScriptIndex); + Ar.Logf(TEXT("%s (label: 0x%X)"), *Indents, ScriptIndex); + DropIndent(); + break; + } + case EX_ArrayGetByRef: + { + Ar.Logf(TEXT("%s $%X: Array Get-by-Ref Index"), *Indents, (int32)Opcode); + AddIndent(); + SerializeExpr(ScriptIndex); + SerializeExpr(ScriptIndex); + DropIndent(); + break; + } + default: + { + // This should never occur. + // UE_LOG(LogScriptDisassembler, Warning, TEXT("Unknown bytecode 0x%02X; ignoring it"), (uint8)Opcode); + break; + } + } + */ +} \ No newline at end of file diff --git a/Project Reboot 3.0/ScriptDisassembler.h b/Project Reboot 3.0/ScriptDisassembler.h new file mode 100644 index 0000000..28ffe60 --- /dev/null +++ b/Project Reboot 3.0/ScriptDisassembler.h @@ -0,0 +1,50 @@ +#pragma once + +#include "Array.h" + +#include + +#include "reboot.h" + +class FKismetBytecodeDisassembler +{ +private: + std::vector Script; + std::string Indents; + std::ofstream Stream; +public: + void DisassembleStructure(UFunction* Source); + + int32 ReadINT(int32& ScriptIndex); + uint64 ReadQWORD(int32& ScriptIndex); + uint8 ReadBYTE(int32& ScriptIndex); + std::string ReadName(int32& ScriptIndex); + uint16 ReadWORD(int32& ScriptIndex); + float ReadFLOAT(int32& ScriptIndex); + CodeSkipSizeType ReadSkipCount(int32& ScriptIndex); + std::string ReadString(int32& ScriptIndex); + std::string ReadString8(int32& ScriptIndex); + std::string ReadString16(int32& ScriptIndex); + + uint8 SerializeExpr(int32& ScriptIndex); + void ProcessCastByte(int32 CastType, int32& ScriptIndex); + void ProcessCommon(int32& ScriptIndex, uint8 Opcode); + + void AddIndent() + { + Indents += (" "); + } + + void DropIndent() + { + // Blah, this is awful + // Indents.LeftInline(Indents.Len() - 2); + Indents = Indents.substr(2); + } + + template + T* ReadPointer(int32& ScriptIndex) + { + return (T*)ReadQWORD(ScriptIndex); + } +}; \ No newline at end of file diff --git a/Project Reboot 3.0/addresses.cpp b/Project Reboot 3.0/addresses.cpp index e37a4d4..978dff6 100644 --- a/Project Reboot 3.0/addresses.cpp +++ b/Project Reboot 3.0/addresses.cpp @@ -457,6 +457,7 @@ void Offsets::FindAll() } Offsets::IsNetRelevantFor = FindIsNetRelevantForOffset(); + Offsets::Script = Offsets::Children + 8 + 4 + 4; } void Offsets::Print() @@ -468,7 +469,7 @@ void Offsets::Print() LOG_INFO(LogDev, "Func: 0x{:x}", Func); LOG_INFO(LogDev, "ServerReplicateActors: 0x{:x}", ServerReplicateActors); LOG_INFO(LogDev, "ReplicationFrame: 0x{:x}", ReplicationFrame); - LOG_INFO(LogDev, "IsNetRelevantFor: 0x{:x}", IsNetRelevantFor); + LOG_INFO(LogDev, "Script: 0x{:x}", Script); } void Addresses::Init() diff --git a/Project Reboot 3.0/addresses.h b/Project Reboot 3.0/addresses.h index 541e0a6..906b507 100644 --- a/Project Reboot 3.0/addresses.h +++ b/Project Reboot 3.0/addresses.h @@ -95,6 +95,7 @@ namespace Offsets extern inline uint64 IsNetRelevantFor = 0; extern inline uint64 NetworkObjectList = 0; extern inline uint64 ClientWorldPackageName = 0; + extern inline uint64 Script = 0; void FindAll(); void Print(); diff --git a/Project Reboot 3.0/ai.h b/Project Reboot 3.0/ai.h index 8e6a3db..beac485 100644 --- a/Project Reboot 3.0/ai.h +++ b/Project Reboot 3.0/ai.h @@ -11,6 +11,9 @@ #include "FortGameModeAthena.h" #include "FortGameStateAthena.h" #include "FortPlayerControllerAthena.h" +#include "FortBotNameSettings.h" +#include "KismetTextLibrary.h" +#include "FortAthenaAIBotCustomizationData.h" using UNavigationSystemV1 = UObject; using UNavigationSystemConfig = UObject; @@ -214,7 +217,7 @@ static void SetupNavConfig(const FName& AgentName) AthenaNavConfig->Get("bSpawnNavDataInNavBoundsLevel") = true; // BITFIELD static auto bUseNavigationInvokersOffset = AthenaNavConfig->GetOffset("bUseNavigationInvokers", false); - + if (bUseNavigationInvokersOffset != -1) AthenaNavConfig->Get(bUseNavigationInvokersOffset) = false; @@ -229,7 +232,7 @@ static void SetupNavConfig(const FName& AgentName) SetNavigationSystem(NavSystemOverride); } -static AFortPlayerPawn* SpawnAIFromCustomizationData(const FVector& Location, UObject* CustomizationData) +static AFortPlayerPawn* SpawnAIFromCustomizationData(const FVector& Location, UFortAthenaAIBotCustomizationData* CustomizationData) { static auto PawnClassOffset = CustomizationData->GetOffset("PawnClass"); auto PawnClass = CustomizationData->Get(PawnClassOffset); @@ -242,12 +245,38 @@ static AFortPlayerPawn* SpawnAIFromCustomizationData(const FVector& Location, UO auto Pawn = GetWorld()->SpawnActor(PawnClass, Location); + if (!Pawn) + { + LOG_INFO(LogAI, "Failed to spawn pawn!"); + return nullptr; + } + + auto Controller = Pawn->GetController(); + + if (!Controller) + { + LOG_INFO(LogAI, "No controller!"); + Pawn->K2_DestroyActor(); + return nullptr; + } + + auto PlayerState = Controller->GetPlayerState(); + + if (!PlayerState) + { + LOG_INFO(LogAI, "No PlayerState!"); + Controller->K2_DestroyActor(); + Pawn->K2_DestroyActor(); + return nullptr; + } + static auto CharacterCustomizationOffset = CustomizationData->GetOffset("CharacterCustomization"); auto CharacterCustomization = CustomizationData->Get(CharacterCustomizationOffset); auto CharacterCustomizationLoadoutOffset = CharacterCustomization->GetOffset("CustomizationLoadout"); auto CharacterCustomizationLoadout = CharacterCustomization->GetPtr(CharacterCustomizationLoadoutOffset); + auto CharacterToApply = CharacterCustomizationLoadout->GetCharacter(); - ApplyCID(Pawn, CharacterCustomizationLoadout->GetCharacter()); + ApplyCID(Pawn, CharacterToApply, true); // bruhh struct FItemAndCount { @@ -259,30 +288,77 @@ static AFortPlayerPawn* SpawnAIFromCustomizationData(const FVector& Location, UO static auto StartupInventoryOffset = CustomizationData->GetOffset("StartupInventory"); auto StartupInventory = CustomizationData->Get(StartupInventoryOffset); static auto StartupInventoryItemsOffset = StartupInventory->GetOffset("Items"); - auto& StartupInventoryItems = StartupInventory->Get>(StartupInventoryItemsOffset); - auto Controller = Pawn->GetController(); - LOG_INFO(LogDev, "Controller: {} StartupInventoryItems.Num: {}", Controller ? Controller->GetFullName() : "InvalidObject", StartupInventoryItems.Num()); + std::vector> ItemsToGrant; - if (Controller) + if (Fortnite_Version < 13) { - /* static auto InventoryOffset = Controller->GetOffset("Inventory"); - auto Inventory = Controller->Get(InventoryOffset); + auto& StartupInventoryItems = StartupInventory->Get>(StartupInventoryItemsOffset); for (int i = 0; i < StartupInventoryItems.Num(); i++) { - auto pair = Inventory->AddItem(StartupInventoryItems.at(i).Item, nullptr, StartupInventoryItems.at(i).Count); + ItemsToGrant.push_back({ StartupInventoryItems.at(i), 1 }); + } + } + else + { + auto& StartupInventoryItems = StartupInventory->Get>(StartupInventoryItemsOffset); + + for (int i = 0; i < StartupInventoryItems.Num(); i++) + { + ItemsToGrant.push_back({ StartupInventoryItems.at(i).Item, StartupInventoryItems.at(i).Count }); + } + } + + static auto InventoryOffset = Controller->GetOffset("Inventory"); + auto Inventory = Controller->Get(InventoryOffset); + + if (Inventory) + { + for (int i = 0; i < ItemsToGrant.size(); i++) + { + auto pair = Inventory->AddItem(ItemsToGrant.at(i).first, nullptr, ItemsToGrant.at(i).second); LOG_INFO(LogDev, "pair.first.size(): {}", pair.first.size()); if (pair.first.size() > 0) { - if (auto weaponDef = Cast(StartupInventoryItems.at(i).Item)) + if (auto weaponDef = Cast(ItemsToGrant.at(i).first)) Pawn->EquipWeaponDefinition(weaponDef, pair.first.at(0)->GetItemEntry()->GetItemGuid()); } - } */ + } - // Inventory->Update(); // crashes idk why + Inventory->Update(); + } + + static auto BotNameSettingsOffset = CustomizationData->GetOffset("BotNameSettings"); + auto BotNameSettings = CustomizationData->Get(BotNameSettingsOffset); + + FString Name; + + if (BotNameSettings) + { + static int CurrentId = 0; // scuffed! + static auto DisplayNameOffset = FindOffsetStruct("/Script/FortniteGame.FortItemDefinition", "DisplayName"); + + switch (BotNameSettings->GetNamingMode()) + { + case EBotNamingMode::Custom: + Name = UKismetTextLibrary::Conv_TextToString(BotNameSettings->GetOverrideName()); + break; + case EBotNamingMode::SkinName: + Name = CharacterToApply ? UKismetTextLibrary::Conv_TextToString(*(FText*)(__int64(CharacterCustomizationLoadout->GetCharacter()) + DisplayNameOffset)) : L"InvalidCharacter"; + Name.Set((std::wstring(Name.Data.Data) += std::to_wstring(CurrentId++)).c_str()); + break; + default: + Name = L"Unknown"; + break; + } + } + + if (Name.Data.Data && Name.Data.Num() > 0) + { + Controller->ServerChangeName(Name); } return Pawn; diff --git a/Project Reboot 3.0/bots.h b/Project Reboot 3.0/bots.h index 1fe93fa..64ba3d4 100644 --- a/Project Reboot 3.0/bots.h +++ b/Project Reboot 3.0/bots.h @@ -252,6 +252,9 @@ namespace Bots static void Tick() { + if (AllPlayerBotsToTick.size() == 0) + return; + auto GameState = Cast(GetWorld()->GetGameState()); auto GameMode = Cast(GetWorld()->GetGameMode()); @@ -310,4 +313,9 @@ namespace Bots // AllBuildingContainers.Free(); } +} + +namespace Bosses +{ + } \ No newline at end of file diff --git a/Project Reboot 3.0/commands.h b/Project Reboot 3.0/commands.h index 429eb9c..50d5090 100644 --- a/Project Reboot 3.0/commands.h +++ b/Project Reboot 3.0/commands.h @@ -10,6 +10,7 @@ #include "FortLootPackage.h" #include "bots.h" #include "FortAthenaMutator_Bots.h" +#include "ai.h" bool IsOperator(APlayerState* PlayerState, AFortPlayerController* PlayerController) { @@ -646,6 +647,43 @@ void ServerCheatHook(AFortPlayerControllerAthena* PlayerController, FString Msg) SendMessageToConsole(PlayerController, L"Not a valid class!"); } } + else if (Command == "spawnbottest") + { + // /Game/Athena/AI/MANG/BotData/ + + if (NumArgs < 1) + { + SendMessageToConsole(PlayerController, L"Please provide a customization object!"); + return; + } + + auto Pawn = ReceivingController->GetPawn(); + + if (!Pawn) + { + SendMessageToConsole(PlayerController, L"No pawn to spawn bot at!"); + return; + } + + auto CustomizationData = LoadObject(Arguments[1], UFortAthenaAIBotCustomizationData::StaticClass()); + + if (!CustomizationData) + { + SendMessageToConsole(PlayerController, L"Invalid CustomizationData!"); + return; + } + + auto NewPawn = SpawnAIFromCustomizationData(Pawn->GetActorLocation(), CustomizationData); + + if (NewPawn) + { + SendMessageToConsole(PlayerController, L"Spawned!"); + } + else + { + SendMessageToConsole(PlayerController, L"Failed to spawn!"); + } + } else if (Command == "spawnbot") { auto Pawn = ReceivingController->GetPawn(); diff --git a/Project Reboot 3.0/die.h b/Project Reboot 3.0/die.h index 9b6726a..1639f4a 100644 --- a/Project Reboot 3.0/die.h +++ b/Project Reboot 3.0/die.h @@ -179,11 +179,6 @@ void ProcessEventHook(UObject* Object, UFunction* Function, void* Parameters) if (!Object || !Function) return; - if (bEnableBotTick) - { - Bots::Tick(); - } - if (Globals::bLogProcessEvent) { auto FunctionName = Function->GetName(); // UKismetSystemLibrary::GetPathName(Function).ToString(); diff --git a/Project Reboot 3.0/dllmain.cpp b/Project Reboot 3.0/dllmain.cpp index ec4f67e..0896a9d 100644 --- a/Project Reboot 3.0/dllmain.cpp +++ b/Project Reboot 3.0/dllmain.cpp @@ -761,6 +761,11 @@ DWORD WINAPI Main(LPVOID) Hooking::MinHook::Hook(FortPlayerPawnAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerPawn.ServerSendZiplineState"), AFortPlayerPawn::ServerSendZiplineStateHook, nullptr, false); Hooking::MinHook::Hook((PVOID)GetFunctionIdxOrPtr(FindObject(L"/Script/FortniteGame.FortPlayerPawn.ServerOnExitVehicle"), true), AFortPlayerPawn::ServerOnExitVehicleHook, (PVOID*)&AFortPlayerPawn::ServerOnExitVehicleOriginal); + if (Fortnite_Version == 1.11 || Fortnite_Version > 1.8) + { + Hooking::MinHook::Hook(FortPlayerPawnAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerPawn.ServerReviveFromDBNO"), + AFortPlayerPawn::ServerReviveFromDBNOHook, nullptr, false); + } static auto FortGameplayAbilityAthena_PeriodicItemGrantDefault = FindObject(L"/Script/FortniteGame.Default__FortGameplayAbilityAthena_PeriodicItemGrant"); @@ -1008,12 +1013,6 @@ DWORD WINAPI Main(LPVOID) static auto GameplayEventDataSize = FindObject(L"/Script/GameplayAbilities.GameplayEventData")->GetPropertiesSize(); LOG_INFO(LogDev, "GameplayEventDataSize: 0x{:x} {}", GameplayEventDataSize, GameplayEventDataSize); - if (Fortnite_Version == 1.11 || Fortnite_Version > 1.8) - { - Hooking::MinHook::Hook(FortPlayerPawnAthenaDefault, FindObject(L"/Script/FortniteGame.FortPlayerPawn.ServerReviveFromDBNO"), - AFortPlayerPawn::ServerReviveFromDBNOHook, nullptr, false); - } - { int increaseOffset = 0x10; diff --git a/Project Reboot 3.0/inc.h b/Project Reboot 3.0/inc.h index 55af9a0..23900f7 100644 --- a/Project Reboot 3.0/inc.h +++ b/Project Reboot 3.0/inc.h @@ -12,6 +12,8 @@ typedef short int16; typedef int int32; typedef __int64 int64; typedef unsigned int uint32; +typedef char ANSICHAR; +typedef uint32_t CodeSkipSizeType; typedef unsigned __int64 uint64; extern inline int Engine_Version = 0; // For example, 420, 421, etc. // Prevent using this when possible.