How to generate a new SDK?
To generate a new SDK you need to follow these steps.
1. Choose the engine version your game uses
To make your life easier, you should copy the files from the sample project which uses the same version of the Unreal Engine as the game because you need to make less code changes.
2. Generate new project files
If you have choosen a game to copy from just run the CreateNewProject.bat file. This will run a powershell script which will ask you for the name of the project to copy from and the name of the new project.
Example: You want to generate a SDK for a game called MyCoolGame. MyCoolGame uses the Unreal Engine 3 so you should use Unreal Engine 3 as template project. To create the new project files you execute the CreateNewProject.bat file and enter
> copy from: UnrealEngine3
> new name: MyCoolGame
(Hint: If the batch file is in the default folder you can use TAB to autocomplete the names. Just remove the file extension in the name.)
The batch file copies the content, replaces some text and creates the new project files for you. You can do this manual too but why should you...?
As last step you need to open the UnrealEngineSdkGenerator.sln and add the new MyCoolGame.vcxproj to the solution.
List of generated files
Each game has 5 files you may need to change.
EngineClasses.hpp This file contains the classes which are needed to generate the SDK. The classes are game dependend and usually you need to make changes here.
Generator.cpp This file contains settings which control the SDK generation. The classes are game dependen and usually you need to make changes here.
GenericTypes.cpp This file contains wrapper classes which map the classes from EngineClasses.hpp to an interface which allows the generator to work with every version of Unreal Engine without making changes everywhere. Normaly you don't need to make changes here.
NamesStore.cpp This file contains the game dependend access to the names used by the game. Here you need to provide access to the global names array used by Unreal Engine. With the pattern library you can use signatures to automaticly find the correct adress at runtime.
ObjectsStore.cpp Same as the NamesStore.cpp but with code for the objects array used by Unreal Engine.
3. Reverse engineer the needed classes
To get the classes you need to insert into EngineClasses.hpp I recommend this video https://www.youtube.com/watch?v=yNe4jz1v0R4 from The Feckless and the tool ReClass.NET (https://github.com/KN4CK3R/ReClass.NET). It shows how you can find the name array (put the signature in NamesStore.cpp), the object array (put the signature in ObjectsStore.cpp) and the needed classes (put them in EngineClasses.hpp). Again: To make your life easier, you should use the same member names in the classes so you don't need to change other places too.
4. Implement the project specific code
The Generator.cpp file contains settings which control the SDK generation.
First the easy methods:
GetOutputDirectory()
This method gets the path where the files should be generated. You don't need to add the game name because the name will get automaticly appended.
If you specify an absolute path (ex. "C:/SDK_GEN") the files will get generated there.
If you specify an relative path (ex. "SDK_GEN") the files will get generated in the directory where compiled dll is injected from appended with the relative path.
Example:
The dll gets injected from "C:/UnrealEngineSdkGenerator/bin/MyCoolGame.dll". So the output directory will be "C:/UnrealEngineSdkGenerator/bin/SDK_GEN".
The default value is "C:/SDK_GEN".
GetGameName()
This method should return the full name of the game. (ex. "MyCoolGame")
GetGameNameShort()
This method should return an abbreviation for the full name. The generated files get prefixed with this short name. (ex. "MCG")
GetGameVersion()
This method should return the current version of the game. The version number gets printed to the header of every file. (ex. "1.2.3.4")
ShouldDumpArrays()
If this method returns true (default) the SDK dumper generates two textfiles which contain a list of all names and the names of the objects.
ShouldGenerateEmptyFiles()
If this method returns false (default) no package files are generated when the package doesn't contain classes, constants or enums.
ShouldUseStrings()
If this method returns true (default) the objects are referenced by their name. Otherwise the objects global index will be used.
Warning: The index may change on updates or even on every start of the games.
ShouldXorStrings()
If this method returns true (default: false) the strings printed by the generator get surrounded by _xor_(...)
. With the XorStr library these strings get xor encrypted at compile time.
(Library: https://github.com/KN4CK3R/XorStr)
ShouldConvertStaticMethods()
If this method returns true (default) the static methods of a class are converted to normal methods. Static methods are only syntactic sugar from the engine. They are normal methods but called with the default object of a class. From the user point of view they are static methods but in reality they are not. If you want to use static methods you need to provide a method called CreateDefaultObject()
in the UObject class. Some of the projects have signatures for the engine method but they could be hard to find. ShouldConvertStaticMethods()
allows to rewrite the static methods to work like normal methods which require a class instance to get called. Additionaly these methods get a "STATIC_" prefix.
GetNamespaceName()
To seperated the generated classes from the rest of your project you can surround the classes with a namespace. By default no namespace is generated.
This method should return the name of the namespace. If an empty name is given no namespace will be generated. (ex. "Classes")
The generated output looks like:
namespace Classes
{
...
}
GetIncludes()
If your code needs some special header files to work return them in this method. For example you could need a pattern search library so add the related header file.
Example:
return {
R"("../my/relative/path/patternscan.hpp")", //with the raw literal you don't need to escape the quotes
"\"../math/vector.hpp\"",
"<windows.h>"
};
GetGlobalMemberAlignment()
This method returns the alignment of members. By default this method returns 4 at 32bit and 8 at 64bit.
See https://msdn.microsoft.com/en-us/library/2e70t5y1.aspx for more information.
The following methods are just getters and should not be modified:
GetOverrideType()
This method checks if you have specified an override type for the related type. If no override type is found the original type is returned. This is usefull if you want to map some of your classes into the SDK. For example you could replace the provided FVector
class with a version of your choice. You need to make sure the replacement hast the same size as the original class. If your override type is declared in an other header file you should add the include with the GetIncludes()
method.
GetPredefinedClassMembers()
For some classes the dumper can't generate members. If a class needs to have meaningfull members (for example the important UObject
class) you can specify the members and this class will return them if the classname is matched.
GetPredefinedClassStaticMembers()
Sometimes classes should have static members. This methods returns them if you have specified them.
GetVirtualFunctionPatterns()
If you need virtual methods in a class you can specify patterns to match these methods. The generator loops the virtual methods of the class and checks for pattern matches. If a pattern matches the specific method will be added to the class.
GetPredefinedClassMethods()
If you want to add methods to a class you can specify them and this method returns them.
GetClassAlignas()
Some classes need a special memory alignment (since Unreal Engine 4). This method gets the alignas value for the classes you have specified.
And now the advanced methods:
GetBasicDeclarations()
This method returns a string with code which contains all the basic classes. Usually this is the same code you need to put in the EngineClasses.hpp. You should check the contained projects for examples.
GetBasicDefinitions()
This method returns a string with code which contains definitions for the classes defined by the GetBasicDeclarations()
method. You should check the contained projects for examples.
Initialize()
This is the main method for you! This method initializes all the stuff the other methods need to work. You can do anything here (disable a possible protection, ...) but usually you just need to provide some data the getter methods can return.
It looks complicated but for the best explanation of this method you should have a look at the provided projects.
-
overrideTypes
This map contains the override classes used byGetOverrideType()
.Example: This example forces the generator to override both
FVector
classes with the custom classes.overrideTypes = { { "struct FVector", "class Vector3D" }, { "struct FVector2D", "class Vector2D" } };
-
alignasClasses
This map contains the alignas classes used byGetClassAlignas()
.Example: This example forces the alignas keyword for the three listed classes.
alignasClasses = { { "ScriptStruct CoreUObject.Plane", 16 }, { "ScriptStruct CoreUObject.Vector4", 16 }, { "ScriptStruct Engine.RootMotionSourceGroup", 8 } };
-
virtualFunctionPattern
This map is used byGetVirtualFunctionPatterns()
and contains signatures which are used to find virtual methods in classes.Example: This example adds a signature which if found adds the ProcessEvent method to the Object class.
virtualFunctionPattern["Class CoreUObject.Object"] = { { "\x45\x33\xF6\x4D\x8B\xE0", "xxxxxx", R"(inline void ProcessEvent(class UFunction* function, void* parms) { return GetVFunction<void(*)(UObject*, class UFunction*, void*)>(this, %d)(this, function, parms); })" } };
-
predefinedMembers
This map is used byGetPredefinedClassMembers()
and contains member variables which can't be dumped. At the moment you can't mix auto generated and predefined members. The members are added in the class in the same order as in the map.Example: This example adds the listed members to the Object class.
predefinedMembers["Class CoreUObject.Object"] = { { "void*", "Vtable" }, { "int32_t", "ObjectFlags" }, { "int32_t", "InternalIndex" }, { "class UClass*", "Class" }, { "FName", "Name" }, { "class UObject*", "Outer" } };
-
predefinedStaticMembers
This map is used byGetPredefinedClassStaticMembers()
and contains static member variables.Example: This example adds the static member GObjects with the type FUObjectArray* to the Object class.
predefinedStaticMembers["Class CoreUObject.Object"] = { { "FUObjectArray*", "GObjects" } };
-
predefinedMethods
This map is used byGetPredefinedClassMethods()
and contains custom methods. This can be used to add helper functions like constructors.Example: This example adds two constructors to the Vector2D class to make initialization easier.
predefinedMethods["ScriptStruct CoreUObject.Vector2D"] = { PredefinedMethod::Inline(R"(inline FVector2D() : X(0), Y(0) { })"), PredefinedMethod::Inline(R"(inline FVector2D(float x, float y) : X(x), Y(y) { })") };
Support for custom properties:
Some games add custom properties which are not included in the official Unreal Engine version. Because they are only valid in the project of the game they should not be added to global GenericTypes.hpp/cpp
files. Instead you can add them to the GetCustomPropertyInfo
method in the game specific GenericTypes.cpp
file. In this example the game uses a property UStdStringProperty
which indicates a std::string
member. You would then put the following code into the GetCustomPropertyInfo
method:
//---------------------------------------------------------------------------
//UEStdStringProperty
//---------------------------------------------------------------------------
class UEStdStringProperty : public UEProperty
{
public:
using UEProperty::UEProperty;
Info GetInfo() const
{
return Info::Create(PropertyType::PredefinedStruct, sizeof(std::string), true, "std::string");
}
static UEClass StaticClass()
{
static auto c = ObjectsStore().FindClass("Class CoreUObject.StdStringProperty");
return c;
}
};
bool UEProperty::GetCustomPropertyInfo(const UEProperty& property, Info &info)
{
if (property.IsA<UEStdStringProperty>())
{
info = property.Cast<UEStdStringProperty>().GetInfo();
return true;
}
return false;
}
And now?
Compile the project and inject the DLL into the target. Use the debug build and a debugger to fix errors but use the release build to really generate the sdk. Otherwise you need to wait some minutes because the debug build is very slow. After you see the "Finished!" messagebox you can have a look at your new sdk.
The generated folder structure looks like this:
XXX
+-- Generator.log
+-- NamesDump.txt
+-- ObjectsDump.txt
+-- SDK.hpp
+-- SDK
| +-- XXX_Basic.hpp
| +-- XXX_Basic.cpp
| +-- XXX_..._classes.hpp
| +-- XXX_..._functions.cpp
| +-- XXX_..._structs.hpp
Generator.log
This file contains the log messages the generator outputs.
NamesDump.txt
This file is generated if ShouldDumpArrays()
is true and it contains all names available in the names array.
ObjectsDump.txt
This file is generated if ShouldDumpArrays()
is true and it contains all objects names available in the objects array.
SDK.hpp
This file contains all includes you need for the SDK.
XXX_Basic.hpp / XXX_Basic.cpp
These files contain the code outputted by GetBasicDeclarations()
and GetBasicDefinitions()
.
XXX_..._classes.hpp / XXX_..._structs.hpp
These files contain the code of the classes and structs.
XXX_..._functions.cpp
This file contains the code of the class methods.
The SDK folder contains the generated code for every package. You need to copy the SDK.hpp and the SDK folder to your project. In your code you need an include to the SDK.hpp and add the *.cpp files to the project. Most of the time you don't need all the cpp files. Files you may always need:
XXX_Basic.cpp
XXX_Core(UObject)_functions.cpp
XXX_Engine_functions.cpp
To find these files just sort the SDK folder by file size. The biggest files are the files you are looking for.
Now you can use the SDK in your project. Have fun.
Known Errors
Sometimes you can't compile the generated SDK. Here are some known errors and a solution how to fix them:
- (UE4) In the generated enum EPixelFormat (CoreUObject_classes.hpp) is a PF_MAX value. The windows.h header defines a constant with the same name so this will keep you from compiling. Just remove the PF_MAX value from enum and all should work.
- Sometimes class members or method parameters get a name like "return", "continue", "break" which are reserved keywords so the compiler will complain. To fix this error just rename them to something else.