diff --git a/.gitignore b/.gitignore index 476e03a..b52ab7f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,43 +1,253 @@ -# Prerequisites -*.d +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. -# Compiled Object files -*.slo -*.lo -*.o -*.obj +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates -# Precompiled Headers -*.gch -*.pch +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs -# Linker files +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h *.ilk - -# Debugger Files +*.meta +*.obj +*.pch *.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc -# Compiled Dynamic libraries -*.so -*.dylib -*.dll +# Chutzpah Test files +_Chutzpah* -# Fortran module files -*.mod -*.smod +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile -# Compiled Static libraries -*.lai -*.la -*.a +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap -# Executables -*.exe -*.out -*.app +# TFS 2012 Local Workspace +$tf/ +<<<<<<< Updated upstream # debug information files *.dwo /.vs/UniversalSlashingSimulator/CopilotIndices /.vs /obj +======= +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml +>>>>>>> Stashed changes diff --git a/Engine/CoreTypes/FString.h b/Engine/CoreTypes/FString.h new file mode 100644 index 0000000..38c2d7d --- /dev/null +++ b/Engine/CoreTypes/FString.h @@ -0,0 +1,299 @@ +/** + * UniversalSlashingSimulator - FString Implementation + * + * Version-agnostic FString implementation for reading/writing Unreal Engine strings. + * Supports UE4.16 through UE5.x. + * + * FString in UE is essentially TArray where TCHAR is wchar_t on Windows. + * The memory layout is consistent across all supported versions: + * - Data pointer (TCHAR*) + * - ArrayNum (int32) - number of elements including null terminator + * - ArrayMax (int32) - allocated capacity + */ + +#pragma once + +#include "../../Core/Common.h" +#include "../../Core/Memory/Memory.h" +#include + +namespace USS +{ + /** + * Read-only view of an FString in memory + * Used for reading FString data returned by engine functions + */ + class FStringView + { + public: + FStringView() : m_Data(nullptr), m_Num(0), m_Max(0) {} + + FStringView(const wchar_t* Data, int32 Num, int32 Max) + : m_Data(Data), m_Num(Num), m_Max(Max) {} + + // Read from memory address (where FString struct is located) + static FStringView FromAddress(uintptr_t Address) + { + if (!Address) + return FStringView(); + + FStringView View; + Memory::Read(Address, View.m_Data); + Memory::Read(Address + sizeof(void*), View.m_Num); + Memory::Read(Address + sizeof(void*) + sizeof(int32), View.m_Max); + return View; + } + + bool IsValid() const { return m_Data != nullptr && m_Num > 0; } + bool IsEmpty() const { return m_Num <= 1; } // 1 = just null terminator + + int32 Len() const { return m_Num > 0 ? m_Num - 1 : 0; } + + const wchar_t* GetData() const { return m_Data; } + + std::wstring ToWString() const + { + if (!IsValid() || IsEmpty()) + return std::wstring(); + + std::wstring Result; + Result.reserve(Len()); + + for (int32 i = 0; i < Len(); ++i) + { + wchar_t Ch = 0; + Memory::Read(reinterpret_cast(m_Data) + (i * sizeof(wchar_t)), Ch); + if (Ch == 0) break; + Result += Ch; + } + + return Result; + } + + // Convert to std::string (narrow, ASCII-safe) + std::string ToString() const + { + if (!IsValid() || IsEmpty()) + return std::string(); + + std::string Result; + Result.reserve(Len()); + + for (int32 i = 0; i < Len(); ++i) + { + wchar_t Ch = 0; + Memory::Read(reinterpret_cast(m_Data) + (i * sizeof(wchar_t)), Ch); + if (Ch == 0) break; + // Convert to narrow char (works for ASCII range) + Result += static_cast(Ch & 0xFF); + } + + return Result; + } + + private: + const wchar_t* m_Data; + int32 m_Num; + int32 m_Max; + }; + + /** + * FString structure layout (for direct memory access) + * This matches the in-memory layout of FString across all UE4/5 versions + */ + struct FStringData + { + wchar_t* Data; // Pointer to TCHAR array + int32 ArrayNum; // Number of elements (including null terminator) + int32 ArrayMax; // Allocated capacity + + FStringView AsView() const + { + return FStringView(Data, ArrayNum, ArrayMax); + } + + bool IsValid() const { return Data != nullptr && ArrayNum > 0; } + + std::string ToString() const + { + return AsView().ToString(); + } + + std::wstring ToWString() const + { + return AsView().ToWString(); + } + }; + + /** + * FString wrapper class for creating and managing FString-compatible data + * Can be passed to UE functions expecting FString parameters + */ + class FString + { + public: + FString() : m_Data(nullptr), m_Num(0), m_Max(0) {} + + explicit FString(const char* Str) + { + if (Str && *Str) + { + size_t Len = strlen(Str); + Allocate(static_cast(Len + 1)); + + for (size_t i = 0; i <= Len; ++i) + { + m_Data[i] = static_cast(Str[i]); + } + m_Num = static_cast(Len + 1); + } + else + { + m_Data = nullptr; + m_Num = 0; + m_Max = 0; + } + } + + explicit FString(const wchar_t* Str) + { + if (Str && *Str) + { + size_t Len = wcslen(Str); + Allocate(static_cast(Len + 1)); + memcpy(m_Data, Str, (Len + 1) * sizeof(wchar_t)); + m_Num = static_cast(Len + 1); + } + else + { + m_Data = nullptr; + m_Num = 0; + m_Max = 0; + } + } + + explicit FString(const std::string& Str) + : FString(Str.c_str()) + {} + + explicit FString(const std::wstring& Str) + : FString(Str.c_str()) + {} + + FString(const FString& Other) + { + if (Other.m_Data && Other.m_Num > 0) + { + Allocate(Other.m_Num); + memcpy(m_Data, Other.m_Data, Other.m_Num * sizeof(wchar_t)); + m_Num = Other.m_Num; + } + else + { + m_Data = nullptr; + m_Num = 0; + m_Max = 0; + } + } + + FString(FString&& Other) noexcept + : m_Data(Other.m_Data) + , m_Num(Other.m_Num) + , m_Max(Other.m_Max) + { + Other.m_Data = nullptr; + Other.m_Num = 0; + Other.m_Max = 0; + } + + ~FString() + { + Free(); + } + + FString& operator=(const FString& Other) + { + if (this != &Other) + { + Free(); + if (Other.m_Data && Other.m_Num > 0) + { + Allocate(Other.m_Num); + memcpy(m_Data, Other.m_Data, Other.m_Num * sizeof(wchar_t)); + m_Num = Other.m_Num; + } + } + return *this; + } + + FString& operator=(FString&& Other) noexcept + { + if (this != &Other) + { + Free(); + m_Data = Other.m_Data; + m_Num = Other.m_Num; + m_Max = Other.m_Max; + Other.m_Data = nullptr; + Other.m_Num = 0; + Other.m_Max = 0; + } + return *this; + } + + bool IsEmpty() const { return m_Num <= 1; } + bool IsValid() const { return m_Data != nullptr; } + int32 Len() const { return m_Num > 0 ? m_Num - 1 : 0; } + const wchar_t* GetData() const { return m_Data; } + wchar_t* GetData() { return m_Data; } + + FStringView AsView() const + { + return FStringView(m_Data, m_Num, m_Max); + } + + std::string ToString() const + { + return AsView().ToString(); + } + + std::wstring ToWString() const + { + return AsView().ToWString(); + } + + // Get pointer to the FString struct data (for passing to UE functions) + // Returns pointer to this object's internal layout which matches FString + void* GetStructPtr() { return &m_Data; } + const void* GetStructPtr() const { return &m_Data; } + + private: + void Allocate(int32 Capacity) + { + Free(); + m_Data = new wchar_t[Capacity]; + m_Max = Capacity; + m_Num = 0; + } + + void Free() + { + if (m_Data) + { + delete[] m_Data; + m_Data = nullptr; + } + m_Num = 0; + m_Max = 0; + } + + // Memory layout must match UE's FString + wchar_t* m_Data; // AllocatorInstance.Data + int32 m_Num; // ArrayNum + int32 m_Max; // ArrayMax + }; + + static_assert(sizeof(FString) == sizeof(void*) + sizeof(int32) * 2, + "FString size mismatch - layout may differ from UE"); + +} \ No newline at end of file