├── .gitignore
├── README.md
├── SDKs
└── BGMI_(v1.8.0)_64Bit.zip
├── Source
├── AndroidManifest.xml
├── jni
│ ├── Android.mk
│ ├── Application.mk
│ └── src
│ │ ├── EngineClasses.hpp
│ │ ├── Generator.cpp
│ │ ├── IGenerator.hpp
│ │ ├── Includes.h
│ │ ├── Logger.cpp
│ │ ├── Logger.hpp
│ │ ├── Main.cpp
│ │ ├── Main.h
│ │ ├── NameValidator.cpp
│ │ ├── NameValidator.hpp
│ │ ├── NamesStore.cpp
│ │ ├── NamesStore.hpp
│ │ ├── ObjectsStore.cpp
│ │ ├── ObjectsStore.hpp
│ │ ├── Package.cpp
│ │ ├── Package.hpp
│ │ ├── PrintHelper.cpp
│ │ ├── PrintHelper.hpp
│ │ ├── Tools.cpp
│ │ ├── Tools.h
│ │ ├── UE4
│ │ ├── FunctionFlags.cpp
│ │ ├── FunctionFlags.hpp
│ │ ├── GenericTypes.cpp
│ │ ├── GenericTypes.hpp
│ │ ├── PropertyFlags.cpp
│ │ └── PropertyFlags.hpp
│ │ ├── cpplinq.hpp
│ │ └── tinyformat.h
├── libs
│ └── arm64-v8a
│ │ └── libnative-lib.so
├── ndk.cmd
├── project.properties
├── res
│ └── values
│ │ └── strings.xml
└── src
│ └── com
│ └── ue4
│ └── sdkgen
│ └── HelloJni.java
└── Target
└── BGMI
├── EngineClasses.hpp
├── Generator.cpp
├── GenericTypes.cpp
├── NamesStore.cpp
└── ObjectsStore.cpp
/.gitignore:
--------------------------------------------------------------------------------
1 | /Source/build
2 | /Source/gen
3 | /Source/bin
4 | /Source/obj
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## UE4-SDK-GENERATOR for Android
2 | UnrealEngine-4 Sdk Generator for Android Devices.
3 | * Generated SDKs [HERE](https://github.com/D-R-99/UE4SDKGenerator/tree/master/SDKs/)
4 | * This Tool is just Converted for Android from @KN4CK3R 's UE4SDKGen.
5 | * Currently Tested on 64Bit Version of BGMI, But can support other games that have EngineVersion from 4.18 to 4.22.
6 |
7 | ## How to Use:
8 |
9 | * Add Apk's pkgname and other details in Main.h.
10 | * Add GName and GObject Offsets in Main.h.
11 | * Change Files in Target Dir according to Game's structure and replace that files in Source Dir.
12 | * Compile all files in the Source Directory (x64 Release).
13 | * Comiple With AIDE(64bit NDK Support) -OR-
14 | * Compile With AndroidStudio (click on ndk.cmd file and RUN, if u have cmd execute plugin Installed.)
15 | * Add Lib in Apk. And add this Smali code to Load Lib
16 |
17 | ```
18 | const-string v0, "native-lib"
19 | invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
20 | ```
21 |
22 | * Make sure Apk have Write Permission in media folder.
23 | * SDK will be Generated in /sdcard/Android/media/PKG-NAME folder.
24 |
25 | ## Notes
26 |
27 | * It will Start generating sdk after 5 second delay, at that time maybe not all objects loaded, so less files will be generated, Increase Delay in Main.cpp to Generate SDK in Lobby.
28 | * 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.
29 | * Most of the time you don't need all the cpp files. Add Needed Headers in SDK.hpp.
30 |
31 | ## ToDo
32 |
33 | * Adding Pattern scan for ProcessEvent.
34 | * Adding Missing Field like "ComponentToWorld".
35 | * Fix FunctionFlags not dumped in xxx_functions.cpp.
36 |
37 | if you have a suggestion on how to improve this sdk, please open an issue.
38 |
--------------------------------------------------------------------------------
/SDKs/BGMI_(v1.8.0)_64Bit.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s4m33r89/UE4SDKGenerator/6f8125f03f9a7fe9ce07b19003e459ba266c7555/SDKs/BGMI_(v1.8.0)_64Bit.zip
--------------------------------------------------------------------------------
/Source/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Source/jni/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH := $(call my-dir)
2 | MAIN_LOCAL_PATH := $(call my-dir)
3 | include $(CLEAR_VARS)
4 |
5 | # Here is the name of your lib.
6 | # When you change the lib name, change also on System.loadLibrary("") under OnCreate method on StaticActivity.java
7 | # Both must have same name
8 | LOCAL_MODULE := native-lib
9 |
10 | # Code optimization
11 | LOCAL_ARM_MODE := arm
12 | LOCAL_CFLAGS := -Wno-error=format-security -fpermissive
13 | LOCAL_CFLAGS += -fno-rtti -fno-exceptions -std=c++14
14 | LOCAL_CPPFLAGS += -ffunction-sections -fdata-sections
15 | LOCAL_LDFLAGS += -Wl,--strip-all
16 |
17 | # Here you add the cpp file
18 | LOCAL_C_INCLUDES += $(MAIN_LOCAL_PATH)
19 | LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/UE4
20 | LOCAL_SRC_FILES := src/Main.cpp \
21 | src/Tools.cpp \
22 | src/Logger.cpp \
23 | src/UE4/FunctionFlags.cpp \
24 | src/UE4/PropertyFlags.cpp \
25 | src/ObjectsStore.cpp \
26 | src/NamesStore.cpp \
27 | src/Generator.cpp \
28 | src/NameValidator.cpp \
29 | src/UE4/GenericTypes.cpp \
30 | src/PrintHelper.cpp \
31 | src/Package.cpp \
32 |
33 |
34 | LOCAL_LDLIBS := -llog -landroid
35 | include $(BUILD_SHARED_LIBRARY)
36 |
--------------------------------------------------------------------------------
/Source/jni/Application.mk:
--------------------------------------------------------------------------------
1 | NDK_TOOLCHAIN_VERSION := 4.9
2 | APP_STL := c++_static
3 | APP_ABI := arm64-v8a
4 |
--------------------------------------------------------------------------------
/Source/jni/src/EngineClasses.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | typedef unsigned char uint8;
8 | typedef unsigned short int uint16;
9 | typedef unsigned int uint32;
10 | typedef unsigned long long uint64;
11 |
12 | // Signed base types.
13 | typedef signed char __int8;
14 | typedef signed short int __int16;
15 | typedef signed int __int32;
16 | typedef signed long long __int64;
17 |
18 | struct FPointer
19 | {
20 | uintptr_t Dummy;
21 | };
22 |
23 | struct FQWord
24 | {
25 | int A;
26 | int B;
27 | };
28 |
29 | struct FName
30 | {
31 | int32_t ComparisonIndex;
32 | int32_t Number;
33 | };
34 |
35 | template
36 | struct TArray
37 | {
38 | friend struct FString;
39 |
40 | public:
41 | TArray()
42 | {
43 | Data = nullptr;
44 | Count = Max = 0;
45 | };
46 |
47 | size_t Num() const
48 | {
49 | return Count;
50 | };
51 |
52 | T& operator[](size_t i)
53 | {
54 | return Data[i];
55 | };
56 |
57 | const T& operator[](size_t i) const
58 | {
59 | return Data[i];
60 | };
61 |
62 | bool IsValidIndex(size_t i) const
63 | {
64 | return i < Num();
65 | }
66 |
67 | private:
68 | T* Data;
69 | int32_t Count;
70 | int32_t Max;
71 | };
72 |
73 | template
74 | class TPair
75 | {
76 | public:
77 | KeyType Key;
78 | ValueType Value;
79 | };
80 |
81 | struct FString : public TArray
82 | {
83 | std::string ToString() const
84 | {
85 | //Add Later
86 | std::string str;
87 | return str;
88 | }
89 | };
90 |
91 | class FScriptInterface
92 | {
93 | private:
94 | UObject* ObjectPointer;
95 | void* InterfacePointer;
96 |
97 | public:
98 | UObject* GetObject() const
99 | {
100 | return ObjectPointer;
101 | }
102 |
103 | UObject*& GetObjectRef()
104 | {
105 | return ObjectPointer;
106 | }
107 |
108 | void* GetInterface() const
109 | {
110 | return ObjectPointer != nullptr ? InterfacePointer : nullptr;
111 | }
112 | };
113 |
114 | template
115 | class TScriptInterface : public FScriptInterface
116 | {
117 | public:
118 | InterfaceType* operator->() const
119 | {
120 | return (InterfaceType*)GetInterface();
121 | }
122 |
123 | InterfaceType& operator*() const
124 | {
125 | return *((InterfaceType*)GetInterface());
126 | }
127 |
128 | operator bool() const
129 | {
130 | return GetInterface() != nullptr;
131 | }
132 | };
133 |
134 | struct FText
135 | {
136 | char UnknownData[0x18];
137 | };
138 |
139 | struct FWeakObjectPtr
140 | {
141 | int32_t ObjectIndex;
142 | int32_t ObjectSerialNumber;
143 | };
144 |
145 | struct FStringAssetReference
146 | {
147 | FString AssetLongPathname;
148 | };
149 |
150 | template
151 | class TPersistentObjectPtr
152 | {
153 | public:
154 | FWeakObjectPtr WeakPtr;
155 | int32_t TagAtLastTest;
156 | TObjectID ObjectID;
157 | };
158 |
159 | class FAssetPtr : public TPersistentObjectPtr
160 | {
161 |
162 | };
163 |
164 | struct FGuid
165 | {
166 | uint32_t A;
167 | uint32_t B;
168 | uint32_t C;
169 | uint32_t D;
170 | };
171 |
172 | struct FUniqueObjectGuid
173 | {
174 | FGuid Guid;
175 | };
176 |
177 | class FLazyObjectPtr : public TPersistentObjectPtr
178 | {
179 |
180 | };
181 |
182 | struct FScriptDelegate
183 | {
184 | unsigned char UnknownData[16];
185 | };
186 |
187 | struct FScriptMulticastDelegate
188 | {
189 | unsigned char UnknownData[16];
190 | };
191 |
192 | class UClass;
193 |
194 | class UObject
195 | {
196 | public:
197 | FPointer VTableObject; //0
198 | int32_t ObjectFlags; //8
199 | int32_t InternalIndex; //c
200 | UClass* Class; //10
201 | FName Name; //18
202 | UObject* Outer; //
203 | };
204 |
205 | class UField : public UObject
206 | {
207 | public:
208 | UField* Next;
209 | };
210 |
211 | class UEnum : public UField
212 | {
213 | public:
214 | FString CppType; //0x0030
215 | TArray> Names; //0x0040
216 | __int64 CppForm; //0x0050
217 | };
218 |
219 | class UStruct : public UField
220 | {
221 | public:
222 | UStruct* SuperField;
223 | UField* Children;
224 | int32_t PropertySize;
225 | int32_t MinAlignment;
226 | char pad_0x0048[0x28];
227 | };
228 |
229 | class UScriptStruct : public UStruct
230 | {
231 | public:
232 | char pad_0x0088[0x10]; //0x0088
233 | };
234 |
235 | class UFunction : public UStruct
236 | {
237 | public:
238 | /*__int32 FunctionFlags; //0x0088
239 | __int16 RepOffset; //0x008C
240 | __int8 NumParms; //0x008E
241 | char pad_0x008F[0x1]; //0x008F
242 | __int16 ParmsSize; //0x0090
243 | __int16 ReturnValueOffset; //0x0092
244 | __int16 RPCId; //0x0094
245 | __int16 RPCResponseId; //0x0096
246 | class UProperty* FirstPropertyToInit; //0x0098
247 | UFunction* EventGraphFunction; //0x00A0
248 | __int32 EventGraphCallOffset; //0x00A8
249 | char pad_0x00AC[0x4]; //0x00AC
250 | void* Func; //0x00B0
251 | */
252 | int32_t FunctionFlags; // 0x0000(0x0000) NOT AUTO-GENERATED PROPERTY
253 | int8_t NumParms; // 0x0000(0x0000) NOT AUTO-GENERATED PROPERTY
254 | int16_t ParmsSize; // 0x0000(0x0000) NOT AUTO-GENERATED PROPERTY
255 | int16_t ReturnValueOffset; // 0x0000(0x0000) NOT AUTO-GENERATED PROPERTY
256 | int16_t RPCId; // 0x0000(0x0000) NOT AUTO-GENERATED PROPERTY
257 | int16_t RPCResponseId; // 0x0000(0x0000) NOT AUTO-GENERATED PROPERTY
258 | char pad[0xC]; // 0x0000(0x0000) NOT AUTO-GENERATED PROPERTY
259 | void* Func; // 0x0000(0x0000) NOT AUTO-GENERATED PROPERTY
260 |
261 | };
262 |
263 | class UClass : public UStruct
264 | {
265 | public:
266 | char pad_0x0088[0x170]; //0x0088
267 | };
268 |
269 | class UProperty : public UField
270 | {
271 | public:
272 | __int32 ArrayDim; //0x0030
273 | __int32 ElementSize; //0x0034
274 | FQWord PropertyFlags; //0x0038
275 | __int32 PropertySize; //0x0040
276 | __int32 Offset; //0x0044
277 | char pad_0x0054[0x28]; //0x0048
278 | };
279 |
280 | class UNumericProperty : public UProperty
281 | {
282 | public:
283 |
284 | };
285 |
286 | class UByteProperty : public UNumericProperty
287 | {
288 | public:
289 | UEnum* Enum; // 0x0088 (0x04)
290 | };
291 |
292 | class UUInt16Property : public UNumericProperty
293 | {
294 | public:
295 |
296 | };
297 |
298 | class UUInt32Property : public UNumericProperty
299 | {
300 | public:
301 |
302 | };
303 |
304 | class UUInt64Property : public UNumericProperty
305 | {
306 | public:
307 |
308 | };
309 |
310 | class UInt8Property : public UNumericProperty
311 | {
312 | public:
313 |
314 | };
315 |
316 | class UInt16Property : public UNumericProperty
317 | {
318 | public:
319 |
320 | };
321 |
322 | class UIntProperty : public UNumericProperty
323 | {
324 | public:
325 |
326 | };
327 |
328 | class UInt64Property : public UNumericProperty
329 | {
330 | public:
331 |
332 | };
333 |
334 | class UFloatProperty : public UNumericProperty
335 | {
336 | public:
337 |
338 | };
339 |
340 | class UDoubleProperty : public UNumericProperty
341 | {
342 | public:
343 |
344 | };
345 |
346 | class UBoolProperty : public UProperty
347 | {
348 | public:
349 | uint8_t FieldSize;
350 | uint8_t ByteOffset;
351 | uint8_t ByteMask;
352 | uint8_t FieldMask;
353 | };
354 |
355 | class UObjectPropertyBase : public UProperty
356 | {
357 | public:
358 | UClass* PropertyClass;
359 | };
360 |
361 | class UObjectProperty : public UObjectPropertyBase
362 | {
363 | public:
364 |
365 | };
366 |
367 | class UClassProperty : public UObjectProperty
368 | {
369 | public:
370 | UClass* MetaClass;
371 | };
372 |
373 | class UInterfaceProperty : public UProperty
374 | {
375 | public:
376 | UClass* InterfaceClass;
377 | };
378 |
379 | class UWeakObjectProperty : public UObjectPropertyBase
380 | {
381 | public:
382 |
383 | };
384 |
385 | class ULazyObjectProperty : public UObjectPropertyBase
386 | {
387 | public:
388 |
389 | };
390 |
391 | class UAssetObjectProperty : public UObjectPropertyBase
392 | {
393 | public:
394 |
395 | };
396 |
397 | class UAssetClassProperty : public UAssetObjectProperty
398 | {
399 | public:
400 | UClass* MetaClass;
401 | };
402 |
403 | class UNameProperty : public UProperty
404 | {
405 | public:
406 |
407 | };
408 |
409 | class UStructProperty : public UProperty
410 | {
411 | public:
412 | UScriptStruct* Struct;
413 | };
414 |
415 | class UStrProperty : public UProperty
416 | {
417 | public:
418 |
419 | };
420 |
421 | class UTextProperty : public UProperty
422 | {
423 | public:
424 |
425 | };
426 |
427 | class UArrayProperty : public UProperty
428 | {
429 | public:
430 | UProperty* Inner;
431 | };
432 |
433 | class UMapProperty : public UProperty
434 | {
435 | public:
436 | UProperty* KeyProp;
437 | UProperty* ValueProp;
438 | };
439 |
440 | class UDelegateProperty : public UProperty
441 | {
442 | public:
443 | UFunction* SignatureFunction;
444 | };
445 |
446 | class UMulticastDelegateProperty : public UProperty
447 | {
448 | public:
449 | UFunction* SignatureFunction;
450 | };
451 |
452 | class UEnumProperty : public UProperty
453 | {
454 | public:
455 | class UNumericProperty* UnderlyingProp; //0x0070
456 | class UEnum* Enum; //0x0078
457 | }; //Size: 0x0080
458 |
--------------------------------------------------------------------------------
/Source/jni/src/Generator.cpp:
--------------------------------------------------------------------------------
1 | #include "IGenerator.hpp"
2 | #include "ObjectsStore.hpp"
3 | #include "NamesStore.hpp"
4 | #include "Main.h"
5 |
6 | class Generator : public IGenerator
7 | {
8 | public:
9 | bool Initialize(void* module) override
10 | {
11 | alignasClasses = {
12 | { "ScriptStruct CoreUObject.Plane", 16 },
13 | { "ScriptStruct CoreUObject.Quat", 16 },
14 | { "ScriptStruct CoreUObject.Transform", 16 },
15 | { "ScriptStruct CoreUObject.Vector4", 16 },
16 |
17 | { "ScriptStruct Engine.RootMotionSourceGroup", 8 }
18 | };
19 |
20 | virtualFunctionPattern["Class CoreUObject.Object"] = {
21 | { "\x45\x33\xF6\x4D\x8B\xE0", "xxxxxx", 71, R"( inline void ProcessEvent(class UFunction* function, void* parms)
22 | {
23 | return GetVFunction(this, %d)(this, function, parms);
24 | })"
25 | ,71 }
26 | };
27 | virtualFunctionPattern["Class CoreUObject.Class"] = {
28 | { "\x4C\x8B\xDC\x57\x48\x81\xEC", "xxxxxxx", 107, R"( inline UObject* CreateDefaultObject()
29 | {
30 | return GetVFunction(this, %d)(this);
31 | })"
32 | ,107 }
33 | };
34 |
35 | predefinedMembers["Class CoreUObject.Object"] = {
36 | { "void*", "Vtable" },
37 | { "int32_t", "ObjectFlags" },
38 | { "int32_t", "InternalIndex" },
39 | { "class UClass*", "Class" },
40 | { "FName", "Name" },
41 | { "class UObject*", "Outer" }
42 | };
43 | predefinedStaticMembers["Class CoreUObject.Object"] = {
44 | { "FUObjectArray*", "GObjects" }
45 | };
46 | predefinedMembers["Class CoreUObject.Field"] = {
47 | { "class UField*", "Next" }
48 | };
49 | predefinedMembers["Class CoreUObject.Struct"] = {
50 | { "class UStruct*", "SuperField" },
51 | { "class UField*", "Children" },
52 | { "int32_t", "PropertySize" },
53 | { "int32_t", "MinAlignment" },
54 | { "unsigned char", "UnknownData0x0048[0x28]" }
55 | };
56 | predefinedMembers["Class CoreUObject.Function"] = {
57 | { "int32_t", "FunctionFlags" },
58 | //{ "int16_t", "RepOffset" },
59 | { "int8_t", "NumParms" },
60 | { "int16_t", "ParmsSize" },
61 | { "int16_t", "ReturnValueOffset" },
62 | { "int16_t", "RPCId" },
63 | { "int16_t", "RPCResponseId" },
64 | //{ "class UProperty*", "FirstPropertyToInit" },
65 | //{ "class UFunction*", "EventGraphFunction" },
66 | //{ "int32_t", "EventGraphCallOffset" },
67 | { "unsigned char", "UnknownData0x00c[0xC]" },
68 | { "void*", "Func" }
69 | };
70 |
71 | predefinedMethods["ScriptStruct CoreUObject.Vector2D"] = {
72 | PredefinedMethod::Inline(R"( inline FVector2D()
73 | : X(0), Y(0)
74 | { })"),
75 | PredefinedMethod::Inline(R"( inline FVector2D(float x, float y)
76 | : X(x),
77 | Y(y)
78 | { })")
79 | };
80 | predefinedMethods["ScriptStruct CoreUObject.LinearColor"] = {
81 | PredefinedMethod::Inline(R"( inline FLinearColor()
82 | : R(0), G(0), B(0), A(0)
83 | { })"),
84 | PredefinedMethod::Inline(R"( inline FLinearColor(float r, float g, float b, float a)
85 | : R(r),
86 | G(g),
87 | B(b),
88 | A(a)
89 | { })")
90 | };
91 |
92 | predefinedMethods["Class CoreUObject.Object"] = {
93 | PredefinedMethod::Inline(R"( static inline TUObjectArray& GetGlobalObjects()
94 | {
95 | return GObjects->ObjObjects;
96 | })"),
97 | PredefinedMethod::Default("std::string GetName() const", R"(std::string UObject::GetName() const
98 | {
99 | std::string name(Name.GetName());
100 | if (Name.Number > 0)
101 | {
102 | name += '_' + std::to_string(Name.Number);
103 | }
104 |
105 | auto pos = name.rfind('/');
106 | if (pos == std::string::npos)
107 | {
108 | return name;
109 | }
110 |
111 | return name.substr(pos + 1);
112 | })"),
113 | PredefinedMethod::Default("std::string GetFullName() const", R"(std::string UObject::GetFullName() const
114 | {
115 | std::string name;
116 |
117 | if (Class != nullptr)
118 | {
119 | std::string temp;
120 | for (auto p = Outer; p; p = p->Outer)
121 | {
122 | temp = p->GetName() + "." + temp;
123 | }
124 |
125 | name = Class->GetName();
126 | name += " ";
127 | name += temp;
128 | name += GetName();
129 | }
130 |
131 | return name;
132 | })"),
133 | PredefinedMethod::Inline(R"( template
134 | static T* FindObject(const std::string& name)
135 | {
136 | for (int i = 0; i < GetGlobalObjects().Num(); ++i)
137 | {
138 | auto object = GetGlobalObjects().GetByIndex(i);
139 |
140 | if (object == nullptr)
141 | {
142 | continue;
143 | }
144 |
145 | if (object->GetFullName() == name)
146 | {
147 | return static_cast(object);
148 | }
149 | }
150 | return nullptr;
151 | })"),
152 | PredefinedMethod::Inline(R"( static UClass* FindClass(const std::string& name)
153 | {
154 | return FindObject(name);
155 | })"),
156 | PredefinedMethod::Inline(R"( template
157 | static T* GetObjectCasted(std::size_t index)
158 | {
159 | return static_cast(GetGlobalObjects().GetByIndex(index));
160 | })"),
161 | PredefinedMethod::Default("bool IsA(UClass* cmp) const", R"(bool UObject::IsA(UClass* cmp) const
162 | {
163 | for (auto super = Class; super; super = static_cast(super->SuperField))
164 | {
165 | if (super == cmp)
166 | {
167 | return true;
168 | }
169 | }
170 |
171 | return false;
172 | })")
173 | };
174 | predefinedMethods["Class CoreUObject.Class"] = {
175 | PredefinedMethod::Inline(R"( template
176 | inline T* CreateDefaultObject()
177 | {
178 | return static_cast(CreateDefaultObject());
179 | })")
180 | };
181 |
182 | return true;
183 | }
184 |
185 | std::string GetGameName() const override
186 | {
187 | return gameFullName;
188 | }
189 |
190 | std::string GetGameNameShort() const override
191 | {
192 | return gameShortName;
193 | }
194 |
195 | std::string GetGameVersion() const override
196 | {
197 | return gameVersion;
198 | }
199 |
200 | std::string GetNamespaceName() const override
201 | {
202 | return "SDK";
203 | }
204 |
205 | std::vector GetIncludes() const override
206 | {
207 | return { };
208 | }
209 |
210 | std::string GetBasicDeclarations() const override
211 | {
212 | return R"(template
213 | inline Fn GetVFunction(void *thiz, int idx)
214 | {
215 | auto VTable = *reinterpret_cast(const_cast(thiz));
216 | return (Fn)(VTable[idx]);
217 | }
218 |
219 | class UObject;
220 |
221 | class FUObjectItem
222 | {
223 | public:
224 | UObject* Object;
225 | int32_t Flags;
226 | int32_t ClusterIndex;
227 | int32_t SerialNumber;
228 |
229 | enum class ObjectFlags : int32_t
230 | {
231 | None = 0,
232 | Native = 1 << 25,
233 | Async = 1 << 26,
234 | AsyncLoading = 1 << 27,
235 | Unreachable = 1 << 28,
236 | PendingKill = 1 << 29,
237 | RootSet = 1 << 30,
238 | NoStrongReference = 1 << 31
239 | };
240 |
241 | inline bool IsUnreachable() const
242 | {
243 | return !!(Flags & static_cast>(ObjectFlags::Unreachable));
244 | }
245 | inline bool IsPendingKill() const
246 | {
247 | return !!(Flags & static_cast>(ObjectFlags::PendingKill));
248 | }
249 | };
250 |
251 | class TUObjectArray
252 | {
253 | public:
254 | inline int32_t Num() const
255 | {
256 | return NumElements;
257 | }
258 |
259 | inline UObject* GetByIndex(int32_t index) const
260 | {
261 | return Objects[index].Object;
262 | }
263 |
264 | inline FUObjectItem* GetItemByIndex(int32_t index) const
265 | {
266 | if (index < NumElements)
267 | {
268 | return &Objects[index];
269 | }
270 | return nullptr;
271 | }
272 |
273 | private:
274 | FUObjectItem* Objects;
275 | int32_t MaxElements;
276 | int32_t NumElements;
277 | };
278 |
279 | class FUObjectArray
280 | {
281 | public:
282 | int32_t ObjFirstGCIndex;
283 | int32_t ObjLastNonGCIndex;
284 | int32_t MaxObjectsNotConsideredByGC;
285 | int32_t OpenForDisregardForGC;
286 | TUObjectArray ObjObjects;
287 | };
288 |
289 | template
290 | struct TArray
291 | {
292 | friend struct FString;
293 |
294 | public:
295 | inline TArray()
296 | {
297 | Data = nullptr;
298 | Count = Max = 0;
299 | };
300 |
301 | inline int Num() const
302 | {
303 | return Count;
304 | };
305 |
306 | inline T& operator[](int i)
307 | {
308 | return Data[i];
309 | };
310 |
311 | inline const T& operator[](int i) const
312 | {
313 | return Data[i];
314 | };
315 |
316 | inline bool IsValidIndex(int i) const
317 | {
318 | return i < Num();
319 | }
320 |
321 | private:
322 | T* Data;
323 | int32_t Count;
324 | int32_t Max;
325 | };
326 |
327 | class FNameEntry
328 | {
329 | public:
330 | static const auto NAME_WIDE_MASK = 0x1;
331 | static const auto NAME_INDEX_SHIFT = 1;
332 |
333 | int32_t Index;
334 | #if defined(__LP64__)
335 | char pad[0x8];
336 | #else
337 | char pad[0x4];
338 | #endif
339 |
340 | union
341 | {
342 | char AnsiName[1024];
343 | wchar_t WideName[1024];
344 | };
345 |
346 | inline const int32_t GetIndex() const
347 | {
348 | return Index >> NAME_INDEX_SHIFT;
349 | }
350 |
351 | inline bool IsWide() const
352 | {
353 | return Index & NAME_WIDE_MASK;
354 | }
355 |
356 | inline const char* GetAnsiName() const
357 | {
358 | return AnsiName;
359 | }
360 |
361 | inline const wchar_t* GetWideName() const
362 | {
363 | return WideName;
364 | }
365 | };
366 |
367 | template
368 | class TStaticIndirectArrayThreadSafeRead
369 | {
370 | public:
371 | inline size_t Num() const
372 | {
373 | return NumElements;
374 | }
375 |
376 | inline bool IsValidIndex(int32_t index) const
377 | {
378 | return index < Num() && index > 0;
379 | }
380 |
381 | inline ElementType const* const& operator[](int32_t index) const
382 | {
383 | return *GetItemPtr(index);
384 | }
385 |
386 | private:
387 | inline ElementType const* const* GetItemPtr(int32_t Index) const
388 | {
389 | int32_t ChunkIndex = Index / ElementsPerChunk;
390 | int32_t WithinChunkIndex = Index % ElementsPerChunk;
391 | ElementType** Chunk = Chunks[ChunkIndex];
392 | return Chunk + WithinChunkIndex;
393 | }
394 |
395 | enum
396 | {
397 | ChunkTableSize = (MaxTotalElements + ElementsPerChunk - 1) / ElementsPerChunk
398 | };
399 |
400 | ElementType** Chunks[ChunkTableSize];
401 | int32_t NumElements;
402 | int32_t NumChunks;
403 | };
404 |
405 | using TNameEntryArray = TStaticIndirectArrayThreadSafeRead;
406 |
407 | struct FName
408 | {
409 | union
410 | {
411 | struct
412 | {
413 | int32_t ComparisonIndex;
414 | int32_t Number;
415 | };
416 | };
417 |
418 | inline FName()
419 | : ComparisonIndex(0),
420 | Number(0)
421 | {
422 | };
423 |
424 | inline FName(int32_t i)
425 | : ComparisonIndex(i),
426 | Number(0)
427 | {
428 | };
429 |
430 | FName(const char* nameToFind)
431 | : ComparisonIndex(0),
432 | Number(0)
433 | {
434 | static std::unordered_set cache;
435 |
436 | for (auto i : cache)
437 | {
438 | if (!std::strcmp(GetNames()[i]->GetAnsiName(), nameToFind))
439 | {
440 | ComparisonIndex = i;
441 |
442 | return;
443 | }
444 | }
445 |
446 | for (auto i = 0; i < GetNames().Num(); ++i)
447 | {
448 | if (GetNames()[i] != nullptr)
449 | {
450 | if (!std::strcmp(GetNames()[i]->GetAnsiName(), nameToFind))
451 | {
452 | cache.insert(i);
453 |
454 | ComparisonIndex = i;
455 |
456 | return;
457 | }
458 | }
459 | }
460 | };
461 |
462 | static TNameEntryArray *GNames;
463 | static inline TNameEntryArray& GetNames()
464 | {
465 | return *GNames;
466 | };
467 |
468 | inline const char* GetName() const
469 | {
470 | return GetNames()[ComparisonIndex]->GetAnsiName();
471 | };
472 |
473 | inline bool operator==(const FName &other) const
474 | {
475 | return ComparisonIndex == other.ComparisonIndex;
476 | };
477 | };
478 |
479 | struct FString : private TArray
480 | {
481 | inline FString()
482 | {
483 | }
484 |
485 | FString(const std::wstring s)
486 | {
487 | Max = Count = !s.empty() ? (s.length() * 2) + 1 : 0;
488 | if (Count)
489 | {
490 | Data = (unsigned short *)(s.data());
491 | }
492 | }
493 |
494 | FString(const wchar_t *s) : FString(std::wstring(s)) {
495 | }
496 |
497 | FString(const wchar_t *s, int len) : FString(std::wstring(s, s + len)) {
498 | }
499 |
500 | FString(const std::string s) {
501 | std::wstring_convert> converter;
502 | std::wstring ws = converter.from_bytes(s);
503 |
504 | Max = Count = !ws.empty() ? (ws.length() * 2) + 1 : 0;
505 | if (Count)
506 | {
507 | Data = (unsigned short *)(ws.data());
508 | }
509 | }
510 |
511 | FString(const char *s) : FString(std::string(s)) {
512 | }
513 |
514 | FString(const char *s, int len) : FString(std::string(s, s + len)) {
515 | }
516 |
517 | inline bool IsValid() const
518 | {
519 | return Data != nullptr;
520 | }
521 |
522 | inline const wchar_t* ToWString() const
523 | {
524 | wchar_t *output = new wchar_t[Count + 1];
525 |
526 | for (int i = 0; i < Count; i++) {
527 | const char16_t uc = Data[i];
528 | if (uc - 0xd800u >= 2048u) {
529 | output[i] = uc;
530 | } else {
531 | if ((uc & 0xfffffc00) == 0xd800 && (uc & 0xfffffc00) == 0xdc00)
532 | output[i] = (uc << 10) + Data[i] - 0x35fdc00;
533 | else
534 | output[i] = L'?';
535 | }
536 | }
537 |
538 | output[Count] = 0;
539 | return output;
540 | }
541 |
542 | inline const char* ToString() const
543 | {
544 | std::wstring_convert,char16_t> convert;
545 | return convert.to_bytes(std::u16string(Data, Data + Count)).c_str();
546 | }
547 | };
548 |
549 | template
550 | class TEnumAsByte
551 | {
552 | public:
553 | inline TEnumAsByte()
554 | {
555 | }
556 |
557 | inline TEnumAsByte(TEnum _value)
558 | : value(static_cast(_value))
559 | {
560 | }
561 |
562 | explicit inline TEnumAsByte(int32_t _value)
563 | : value(static_cast(_value))
564 | {
565 | }
566 |
567 | explicit inline TEnumAsByte(uint8_t _value)
568 | : value(_value)
569 | {
570 | }
571 |
572 | inline operator TEnum() const
573 | {
574 | return (TEnum)value;
575 | }
576 |
577 | inline TEnum GetValue() const
578 | {
579 | return (TEnum)value;
580 | }
581 |
582 | private:
583 | uint8_t value;
584 | };
585 |
586 | class FScriptInterface
587 | {
588 | private:
589 | UObject* ObjectPointer;
590 | void* InterfacePointer;
591 |
592 | public:
593 | inline UObject* GetObject() const
594 | {
595 | return ObjectPointer;
596 | }
597 |
598 | inline UObject*& GetObjectRef()
599 | {
600 | return ObjectPointer;
601 | }
602 |
603 | inline void* GetInterface() const
604 | {
605 | return ObjectPointer != nullptr ? InterfacePointer : nullptr;
606 | }
607 | };
608 |
609 | template
610 | class TScriptInterface : public FScriptInterface
611 | {
612 | public:
613 | inline InterfaceType* operator->() const
614 | {
615 | return (InterfaceType*)GetInterface();
616 | }
617 |
618 | inline InterfaceType& operator*() const
619 | {
620 | return *((InterfaceType*)GetInterface());
621 | }
622 |
623 | inline operator bool() const
624 | {
625 | return GetInterface() != nullptr;
626 | }
627 | };
628 |
629 | struct FText
630 | {
631 | #if defined(__LP64__)
632 | char pad[24];
633 | #else
634 | char pad[12];
635 | #endif
636 | };
637 |
638 | struct FScriptDelegate
639 | {
640 | char pad[16];
641 | };
642 |
643 | struct FScriptMulticastDelegate
644 | {
645 | #if defined(__LP64__)
646 | char pad[16];
647 | #else
648 | char pad[12];
649 | #endif
650 | };
651 |
652 | template
653 | class TMap
654 | {
655 | #if defined(__LP64__)
656 | char pad[80];
657 | #else
658 | char pad[60];
659 | #endif
660 | };
661 |
662 | struct FWeakObjectPtr
663 | {
664 | public:
665 | inline bool SerialNumbersMatch(FUObjectItem* ObjectItem) const
666 | {
667 | return ObjectItem->SerialNumber == ObjectSerialNumber;
668 | }
669 |
670 | bool IsValid() const;
671 |
672 | UObject* Get() const;
673 |
674 | int32_t ObjectIndex;
675 | int32_t ObjectSerialNumber;
676 | };
677 |
678 | template
679 | struct TWeakObjectPtr : private TWeakObjectPtrBase
680 | {
681 | public:
682 | inline T* Get() const
683 | {
684 | return (T*)TWeakObjectPtrBase::Get();
685 | }
686 |
687 | inline T& operator*() const
688 | {
689 | return *Get();
690 | }
691 |
692 | inline T* operator->() const
693 | {
694 | return Get();
695 | }
696 |
697 | inline bool IsValid() const
698 | {
699 | return TWeakObjectPtrBase::IsValid();
700 | }
701 | };
702 |
703 | template
704 | class TAutoPointer : public TBASE
705 | {
706 | public:
707 | inline operator T*() const
708 | {
709 | return TBASE::Get();
710 | }
711 |
712 | inline operator const T*() const
713 | {
714 | return (const T*)TBASE::Get();
715 | }
716 |
717 | explicit inline operator bool() const
718 | {
719 | return TBASE::Get() != nullptr;
720 | }
721 | };
722 |
723 | template
724 | class TAutoWeakObjectPtr : public TAutoPointer>
725 | {
726 | public:
727 | };
728 |
729 | template
730 | class TPersistentObjectPtr
731 | {
732 | public:
733 | FWeakObjectPtr WeakPtr;
734 | int32_t TagAtLastTest;
735 | TObjectID ObjectID;
736 | };
737 |
738 | struct FStringAssetReference_
739 | {
740 |
741 | };
742 |
743 | class FAssetPtr : public TPersistentObjectPtr
744 | {
745 |
746 | };
747 |
748 | template
749 | class TAssetPtr : FAssetPtr
750 | {
751 |
752 | };
753 |
754 | struct FUniqueObjectGuid_
755 | {
756 |
757 | };
758 |
759 | class FLazyObjectPtr : public TPersistentObjectPtr
760 | {
761 |
762 | };
763 |
764 | template
765 | class TLazyObjectPtr : FLazyObjectPtr
766 | {
767 |
768 | };)";
769 | }
770 |
771 | std::string GetBasicDefinitions() const override
772 | {
773 | return R"(TNameEntryArray* FName::GNames = nullptr;
774 | FUObjectArray* UObject::GUObjectArray = nullptr;
775 | //---------------------------------------------------------------------------
776 | bool FWeakObjectPtr::IsValid() const
777 | {
778 | if (ObjectSerialNumber == 0)
779 | {
780 | return false;
781 | }
782 | if (ObjectIndex < 0)
783 | {
784 | return false;
785 | }
786 | auto ObjectItem = UObject::GetGlobalObjects().GetItemByIndex(ObjectIndex);
787 | if (!ObjectItem)
788 | {
789 | return false;
790 | }
791 | if (!SerialNumbersMatch(ObjectItem))
792 | {
793 | return false;
794 | }
795 | return !(ObjectItem->IsUnreachable() || ObjectItem->IsPendingKill());
796 | }
797 | //---------------------------------------------------------------------------
798 | UObject* FWeakObjectPtr::Get() const
799 | {
800 | if (IsValid())
801 | {
802 | auto ObjectItem = UObject::GetGlobalObjects().GetItemByIndex(ObjectIndex);
803 | if (ObjectItem)
804 | {
805 | return ObjectItem->Object;
806 | }
807 | }
808 | return nullptr;
809 | }
810 | //---------------------------------------------------------------------------)";
811 | }
812 | };
813 |
814 | Generator _generator;
815 | IGenerator* generator = &_generator;
816 |
--------------------------------------------------------------------------------
/Source/jni/src/IGenerator.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | //#include "Dyno.h"
8 |
9 | class IGenerator
10 | {
11 | public:
12 | virtual ~IGenerator() = default;
13 |
14 | ///
15 | /// Initializes this object.
16 | /// Add predefined types, ...
17 | ///
18 | /// The module handle.
19 | /// true if it succeeds, false if it fails.
20 | virtual bool Initialize(void* module) = 0;
21 |
22 | ///
23 | /// Gets output directory where the files are getting stored.
24 | /// The name of the game gets appended to this directory.
25 | ///
26 | /// The output directory.
27 | virtual std::string GetOutputDirectory(std::string pkgName1) const
28 | {
29 | //std::string pkgName1 = "com.pubg.imobile";
30 | std::string Media_Folder1 = "/storage/emulated/0/Android/media/" + pkgName1;
31 | return Media_Folder1;
32 | }
33 |
34 | ///
35 | /// Gets the name of the game.
36 | ///
37 | /// The game name.
38 | virtual std::string GetGameName() const = 0;
39 |
40 | ///
41 | /// Gets the short name of the game.
42 | ///
43 | /// The short name.
44 | virtual std::string GetGameNameShort() const = 0;
45 |
46 | ///
47 | /// Gets the version of the game.
48 | ///
49 | /// The version of the game.
50 | virtual std::string GetGameVersion() const = 0;
51 |
52 | ///
53 | /// Check if the generator should dump the object and name arrays.
54 | ///
55 | /// true if the arrays should get dumped.
56 | virtual bool ShouldDumpArrays() const
57 | {
58 | return true;
59 | }
60 |
61 | ///
62 | /// Check if the generator should generate empty files (no classes, structs, ...).
63 | ///
64 | /// true if empty files should get generated.
65 | virtual bool ShouldGenerateEmptyFiles() const
66 | {
67 | return false;
68 | }
69 |
70 | ///
71 | /// Check if the generated classes should use strings to identify objects.
72 | /// If false the generated classes use the object index.
73 | /// Warning: The object index may change for non default classes.
74 | ///
75 | /// true if strings should be used.
76 | virtual bool ShouldUseStrings() const
77 | {
78 | return true;
79 | }
80 |
81 | ///
82 | /// Check if strings () should be xor encoded.
83 | ///
84 | /// true if string should be xor encoded.
85 | virtual bool ShouldXorStrings() const
86 | {
87 | return false;
88 | }
89 |
90 | ///
91 | /// Check if static methods should get converted to normal methods.
92 | /// Static methods require a CreateDefaultObject() method in the UObject class.
93 | ///
94 | /// true if static methods should get converted to normal methods.
95 | virtual bool ShouldConvertStaticMethods() const
96 | {
97 | return true;
98 | }
99 |
100 | ///
101 | /// Check if we should generate a function parameters file.
102 | /// Otherwise the parameters are declared inside the function body.
103 | /// If hooks with access to the parameters are need, this method should return true.
104 | ///
105 | /// True if a function parameters file should be generated.
106 | virtual bool ShouldGenerateFunctionParametersFile() const
107 | {
108 | return true;
109 | }
110 |
111 | ///
112 | /// Gets namespace name for the classes. If the name is empty no namespace gets generated.
113 | ///
114 | /// The namespace name.
115 | virtual std::string GetNamespaceName() const
116 | {
117 | return std::string();
118 | }
119 |
120 | ///
121 | /// Gets a list of custom include files which gets inserted in the SDK.
122 | ///
123 | /// The list of include files.
124 | virtual std::vector GetIncludes() const
125 | {
126 | return {};
127 | }
128 |
129 | ///
130 | /// Gets the member alignment.
131 | /// https://msdn.microsoft.com/en-us/library/2e70t5y1.aspx
132 | ///
133 | /// The member alignment.
134 | virtual size_t GetGlobalMemberAlignment() const
135 | {
136 | return sizeof(size_t);
137 | }
138 |
139 | ///
140 | /// Gets alignas size for the specific class.
141 | /// http://cppreference.com/w/cpp/language/alignas
142 | ///
143 | /// The name.
144 | /// If the class is not found the return value is 0, else the alignas size.
145 | virtual size_t GetClassAlignas(const std::string& name) const
146 | {
147 | auto it = alignasClasses.find(name);
148 | if (it != std::end(alignasClasses))
149 | {
150 | return it->second;
151 | }
152 | return 0;
153 | }
154 |
155 | ///
156 | /// Gets the declarations of some basic classes and methods.
157 | ///
158 | /// The basic declarations.
159 | virtual std::string GetBasicDeclarations() const
160 | {
161 | return std::string();
162 | }
163 |
164 | ///
165 | /// Gets the definitions of the declarations.
166 | ///
167 | /// The basic definitions.
168 | virtual std::string GetBasicDefinitions() const
169 | {
170 | return std::string();
171 | }
172 |
173 | ///
174 | /// Checks if an override is defined for the given type.
175 | ///
176 | /// The parameter type.
177 | /// If no override is found the original name is returned.
178 | virtual std::string GetOverrideType(const std::string& type) const
179 | {
180 | auto it = overrideTypes.find(type);
181 | if (it == std::end(overrideTypes))
182 | {
183 | return type;
184 | }
185 | return it->second;
186 | }
187 |
188 | struct PredefinedMember
189 | {
190 | std::string Type;
191 | std::string Name;
192 | };
193 |
194 | ///
195 | /// Gets the predefined members of the specific class.
196 | ///
197 | /// The name of the class.
198 | /// [out] The predefined members.
199 | /// true if predefined members are found.
200 | virtual bool GetPredefinedClassMembers(const std::string& name, std::vector& members) const
201 | {
202 | auto it = predefinedMembers.find(name);
203 | if (it != std::end(predefinedMembers))
204 | {
205 | std::copy(std::begin(it->second), std::end(it->second), std::back_inserter(members));
206 |
207 | return true;
208 | }
209 |
210 | return false;
211 | }
212 |
213 | ///
214 | /// Gets the static predefined members of the specific class.
215 | ///
216 | /// The name of the class.
217 | /// [out] The predefined members.
218 | /// true if predefined members are found.
219 | virtual bool GetPredefinedClassStaticMembers(const std::string& name, std::vector& members) const
220 | {
221 | auto it = predefinedStaticMembers.find(name);
222 | if (it != std::end(predefinedStaticMembers))
223 | {
224 | std::copy(std::begin(it->second), std::end(it->second), std::back_inserter(members));
225 |
226 | return true;
227 | }
228 |
229 | return false;
230 | }
231 |
232 | using VirtualFunctionPatterns = std::vector>;
233 |
234 | ///
235 | /// Gets the patterns of virtual functions of the specific class.
236 | /// The generator loops the virtual functions of the class and adds a class method if the pattern matches.
237 | ///
238 | /// The name of the class.
239 | /// [out] The patterns.
240 | /// true if patterns are found.
241 | virtual bool GetVirtualFunctionPatterns(const std::string& name, VirtualFunctionPatterns& patterns) const
242 | {
243 | auto it = virtualFunctionPattern.find(name);
244 | if (it != std::end(virtualFunctionPattern))
245 | {
246 | std::copy(std::begin(it->second), std::end(it->second), std::back_inserter(patterns));
247 |
248 | return true;
249 | }
250 |
251 | return false;
252 | }
253 |
254 | struct PredefinedMethod
255 | {
256 | enum class Type
257 | {
258 | Default,
259 | Inline
260 | };
261 |
262 | std::string Signature;
263 | std::string Body;
264 | Type MethodType;
265 |
266 | /// Adds a predefined method which gets splittet in declaration and definition.
267 | /// The method signature.
268 | /// The method body.
269 | /// The method.
270 | static PredefinedMethod Default(std::string&& signature, std::string&& body)
271 | {
272 | return { signature, body, Type::Default };
273 | }
274 |
275 | /// Adds a predefined method which gets included as an inline method.
276 | /// The body.
277 | /// The method.
278 | static PredefinedMethod Inline(std::string&& body)
279 | {
280 | return { std::string(), body, Type::Inline };
281 | }
282 | };
283 |
284 | /// Gets the predefined methods of the specific class.
285 | /// The name of the class.
286 | /// [out] The predefined methods.
287 | /// true if predefined methods are found.
288 | virtual bool GetPredefinedClassMethods(const std::string& name, std::vector& methods) const
289 | {
290 | auto it = predefinedMethods.find(name);
291 | if (it != std::end(predefinedMethods))
292 | {
293 | std::copy(std::begin(it->second), std::end(it->second), std::back_inserter(methods));
294 |
295 | return true;
296 | }
297 |
298 | return false;
299 | }
300 |
301 | protected:
302 | std::unordered_map alignasClasses;
303 | std::unordered_map overrideTypes;
304 | std::unordered_map> predefinedMembers;
305 | std::unordered_map> predefinedStaticMembers;
306 | std::unordered_map> predefinedMethods;
307 | std::unordered_map virtualFunctionPattern;
308 | };
309 |
--------------------------------------------------------------------------------
/Source/jni/src/Includes.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDES_H
2 | #define INCLUDES_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | //#include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include