mirror of
https://github.com/ApfelTeeSaft/Alphanium.git
synced 2026-08-26 19:23:26 +00:00
292 lines
8.2 KiB
C++
292 lines
8.2 KiB
C++
#include "UE4Helpers.h"
|
|
#include "UE4Globals.h"
|
|
#include "Memory.h"
|
|
#include "Logger.h"
|
|
#include <windows.h>
|
|
#include <algorithm>
|
|
|
|
UE4Globals g_ue4{};
|
|
|
|
namespace {
|
|
bool IsLikelyNameEntry(const FNameEntry* entry) {
|
|
if (!IsReadableAddress(entry, sizeof(FNameEntry))) {
|
|
return false;
|
|
}
|
|
const char* name = entry->AnsiName;
|
|
if (!IsReadableAddress(name, 4)) {
|
|
return false;
|
|
}
|
|
return name[0] != '\0';
|
|
}
|
|
}
|
|
|
|
FNameEntry* GetNameEntry(int32_t index) {
|
|
if (!g_ue4.GNames) {
|
|
return nullptr;
|
|
}
|
|
auto names = reinterpret_cast<FNameEntryArray*>(g_ue4.GNames);
|
|
if (names && IsReadableAddress(names, sizeof(FNameEntryArray)) && names->Entries) {
|
|
return names->Entries[index];
|
|
}
|
|
auto direct = reinterpret_cast<FNameEntry**>(g_ue4.GNames);
|
|
if (IsReadableAddress(direct, sizeof(void*) * (index + 1))) {
|
|
auto entry = direct[index];
|
|
if (IsLikelyNameEntry(entry)) {
|
|
return entry;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
FUObjectArray* GetGUObjectArray() {
|
|
if (!g_ue4.GUObjectArray) {
|
|
return nullptr;
|
|
}
|
|
auto arr = reinterpret_cast<FUObjectArray*>(g_ue4.GUObjectArray);
|
|
if (IsReadableAddress(arr, sizeof(FUObjectArray))) {
|
|
return arr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
UWorld* GetWorld() {
|
|
return g_ue4.GWorld ? *reinterpret_cast<UWorld**>(g_ue4.GWorld) : nullptr;
|
|
}
|
|
|
|
ProcessEventFn GetProcessEvent() {
|
|
return g_ue4.ProcessEvent ? reinterpret_cast<ProcessEventFn>(g_ue4.ProcessEvent) : nullptr;
|
|
}
|
|
|
|
StaticFindObjectFn GetStaticFindObject() {
|
|
return g_ue4.StaticFindObject ? reinterpret_cast<StaticFindObjectFn>(g_ue4.StaticFindObject) : nullptr;
|
|
}
|
|
|
|
std::string UObject::GetName() const {
|
|
FNameEntry* entry = GetNameEntry(NamePrivate.ComparisonIndex);
|
|
if (!entry) {
|
|
return "None";
|
|
}
|
|
return entry->AnsiName;
|
|
}
|
|
|
|
std::string UObject::GetFullName() const {
|
|
if (!ClassPrivate) {
|
|
return GetName();
|
|
}
|
|
std::string result = ClassPrivate->GetName();
|
|
result += " ";
|
|
const UObject* current = this;
|
|
std::string outerName;
|
|
while (current) {
|
|
outerName = current->GetName();
|
|
if (current->OuterPrivate) {
|
|
outerName = current->OuterPrivate->GetName() + "." + outerName;
|
|
}
|
|
break;
|
|
}
|
|
result += outerName;
|
|
return result;
|
|
}
|
|
|
|
bool UObject::IsA(const UClass* cls) const {
|
|
if (!cls) {
|
|
return false;
|
|
}
|
|
const UClass* current = ClassPrivate;
|
|
while (current) {
|
|
if (current == cls) {
|
|
return true;
|
|
}
|
|
current = current->SuperStruct ? reinterpret_cast<UClass*>(current->SuperStruct) : nullptr;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
FVector AActor::GetActorLocation() const {
|
|
auto* root = reinterpret_cast<const FVector*>(RootComponent);
|
|
if (!root) {
|
|
return {0.0f, 0.0f, 0.0f};
|
|
}
|
|
return *root;
|
|
}
|
|
|
|
UObject* FindObjectByName(const std::wstring& name) {
|
|
if (auto fn = GetStaticFindObject()) {
|
|
return fn(nullptr, nullptr, name.c_str(), false);
|
|
}
|
|
auto* objects = GetGUObjectArray();
|
|
if (!objects || !objects->Objects) {
|
|
return nullptr;
|
|
}
|
|
for (int32_t i = 0; i < objects->NumElements; ++i) {
|
|
auto& item = objects->Objects[i];
|
|
if (!item.Object) {
|
|
continue;
|
|
}
|
|
std::string full = item.Object->GetFullName();
|
|
std::wstring full_w(full.begin(), full.end());
|
|
if (full_w.find(name) != std::wstring::npos) {
|
|
return item.Object;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
UFunction* FindFunction(const std::wstring& name) {
|
|
auto obj = FindObjectByName(name);
|
|
return obj ? reinterpret_cast<UFunction*>(obj) : nullptr;
|
|
}
|
|
|
|
APlayerController* GetLocalPlayerController() {
|
|
auto* objects = GetGUObjectArray();
|
|
if (!objects || !objects->Objects) {
|
|
return nullptr;
|
|
}
|
|
for (int32_t i = 0; i < objects->NumElements; ++i) {
|
|
auto& item = objects->Objects[i];
|
|
if (!item.Object) {
|
|
continue;
|
|
}
|
|
std::string name = item.Object->GetFullName();
|
|
if (name.find("PlayerController") != std::string::npos) {
|
|
return reinterpret_cast<APlayerController*>(item.Object);
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
ACharacter* SpawnDefaultCharacter(const FVector& location) {
|
|
UWorld* world = GetWorld();
|
|
if (!world) {
|
|
return nullptr;
|
|
}
|
|
auto spawnFunc = FindFunction(L"Function Engine.World.SpawnActor");
|
|
if (!spawnFunc) {
|
|
return nullptr;
|
|
}
|
|
UObject* characterClassObj = FindObjectByName(L"Class Engine.Character");
|
|
if (!characterClassObj) {
|
|
characterClassObj = FindObjectByName(L"Class Engine.DefaultPawn");
|
|
}
|
|
if (!characterClassObj) {
|
|
return nullptr;
|
|
}
|
|
struct SpawnParams {
|
|
UClass* Class;
|
|
FVector Location;
|
|
FRotator Rotation;
|
|
AActor* Owner;
|
|
APawn* Instigator;
|
|
bool bNoCollisionFail;
|
|
bool bRemoteOwned;
|
|
AActor* ReturnValue;
|
|
} params{};
|
|
params.Class = reinterpret_cast<UClass*>(characterClassObj);
|
|
params.Location = location;
|
|
params.Rotation = {0.0f, 0.0f, 0.0f};
|
|
params.Owner = nullptr;
|
|
params.Instigator = nullptr;
|
|
params.bNoCollisionFail = true;
|
|
params.bRemoteOwned = false;
|
|
params.ReturnValue = nullptr;
|
|
|
|
if (auto process = GetProcessEvent()) {
|
|
process(reinterpret_cast<UObject*>(world), spawnFunc, ¶ms);
|
|
return reinterpret_cast<ACharacter*>(params.ReturnValue);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void PossessPawn(APlayerController* controller, APawn* pawn) {
|
|
if (!controller || !pawn) {
|
|
return;
|
|
}
|
|
auto possessFunc = FindFunction(L"Function Engine.Controller.Possess");
|
|
if (!possessFunc) {
|
|
return;
|
|
}
|
|
struct Params { APawn* Pawn; } params{pawn};
|
|
if (auto process = GetProcessEvent()) {
|
|
process(reinterpret_cast<UObject*>(controller), possessFunc, ¶ms);
|
|
}
|
|
}
|
|
|
|
void SetActorLocation(AActor* actor, const FVector& location) {
|
|
if (!actor) {
|
|
return;
|
|
}
|
|
auto func = FindFunction(L"Function Engine.Actor.SetActorLocation");
|
|
if (!func) {
|
|
return;
|
|
}
|
|
struct Params {
|
|
FVector NewLocation;
|
|
bool bSweep;
|
|
void* SweepHitResult;
|
|
bool bTeleport;
|
|
bool ReturnValue;
|
|
} params{};
|
|
params.NewLocation = location;
|
|
params.bSweep = false;
|
|
params.bTeleport = true;
|
|
if (auto process = GetProcessEvent()) {
|
|
process(reinterpret_cast<UObject*>(actor), func, ¶ms);
|
|
}
|
|
}
|
|
|
|
void DestroyActor(AActor* actor) {
|
|
if (!actor) {
|
|
return;
|
|
}
|
|
auto func = FindFunction(L"Function Engine.Actor.K2_DestroyActor");
|
|
if (!func) {
|
|
return;
|
|
}
|
|
if (auto process = GetProcessEvent()) {
|
|
process(reinterpret_cast<UObject*>(actor), func, nullptr);
|
|
}
|
|
}
|
|
|
|
void ExecuteConsoleCommand(UObject* worldContext, const std::string& command) {
|
|
if (!worldContext) {
|
|
LogMessage("ExecuteConsoleCommand: world context null for command '%s'", command.c_str());
|
|
return;
|
|
}
|
|
auto func = FindFunction(L"Function Engine.KismetSystemLibrary.ExecuteConsoleCommand");
|
|
if (!func) {
|
|
LogMessage("ExecuteConsoleCommand: KismetSystemLibrary.ExecuteConsoleCommand not found");
|
|
return;
|
|
}
|
|
UObject* kismetObj = FindObjectByName(L"Default__KismetSystemLibrary");
|
|
if (!kismetObj) {
|
|
kismetObj = FindObjectByName(L"Class Engine.KismetSystemLibrary");
|
|
}
|
|
if (!kismetObj) {
|
|
LogMessage("ExecuteConsoleCommand: KismetSystemLibrary object not found");
|
|
return;
|
|
}
|
|
struct FString {
|
|
wchar_t* Data;
|
|
int32_t Count;
|
|
int32_t Max;
|
|
} cmd{};
|
|
std::wstring wide(command.begin(), command.end());
|
|
cmd.Data = const_cast<wchar_t*>(wide.c_str());
|
|
cmd.Count = static_cast<int32_t>(wide.size() + 1);
|
|
cmd.Max = cmd.Count;
|
|
struct Params {
|
|
UObject* WorldContext;
|
|
FString Command;
|
|
APlayerController* SpecificPlayer;
|
|
} params{};
|
|
params.WorldContext = worldContext;
|
|
params.Command = cmd;
|
|
params.SpecificPlayer = nullptr;
|
|
if (auto process = GetProcessEvent()) {
|
|
LogMessage("ExecuteConsoleCommand: '%s' (Kismet=%p Function=%p)", command.c_str(), kismetObj, func);
|
|
process(kismetObj, func, ¶ms);
|
|
} else {
|
|
LogMessage("ExecuteConsoleCommand: ProcessEvent not resolved");
|
|
}
|
|
}
|