diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d1917d --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# MacOS Cache +.DS_Store + +# Cache files +.cache/ + +# Visual Studio cache/options directory +.vs + +# CMake generated files +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +CMakeUserPresets.json +include/version.hpp + +# Build output +build/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..40f70cf --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cmake.configureArgs": [ + "-DCMAKE_SYSTEM_NAME=Windows" + ] +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8479852 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,274 @@ +# Specify the minimum CMake version +cmake_minimum_required(VERSION 3.20) + +# --------------------- +# Project Configuration +# --------------------- + +# Define the project +project(CodeRedGenerator + DESCRIPTION "Unreal Engine 3 SDK Generator" + VERSION 1.2.0 + HOMEPAGE_URL "https://github.com/CodeRedModding/CodeRed-Generator" + LANGUAGES CXX C) + +configure_file(version.hpp.in ${CMAKE_CURRENT_SOURCE_DIR}/include/version.hpp @ONLY) + +# Generate compile_commands.json for IDE integration +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Set C++ language standard to C++20, C17 +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_C_STANDARD 17) +set(CMAKE_C_STANDARD_REQUIRED ON) + +# Organize targets into folders in IDEs that support it +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +# Enable all, extra, and pedantic warnings +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + add_compile_options(/W4) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +else() + message(WARNING "Unknown compiler: ${CMAKE_CXX_COMPILER_ID}. Warning flags not set.") +endif() + +# -------------------- +# Engine Configuration +# -------------------- + +# Auto-detect available engines +file(GLOB ENGINE_DIRS + LIST_DIRECTORIES TRUE + "${CMAKE_CURRENT_SOURCE_DIR}/engine/*") + +set(AVAILABLE_ENGINES "") +foreach(DIR ${ENGINE_DIRS}) + if(IS_DIRECTORY ${DIR}) + get_filename_component(ENGINE_NAME ${DIR} NAME) + list(APPEND AVAILABLE_ENGINES ${ENGINE_NAME}) + endif() +endforeach() + +if(NOT AVAILABLE_ENGINES) + message(FATAL_ERROR "No engine directories found in engine/") +endif() + +# Set default engine to the "Template" engine if it exists, otherwise the first available engine +if("Template" IN_LIST AVAILABLE_ENGINES) + set(DEFAULT_ENGINE "Template") +else() + list(GET AVAILABLE_ENGINES 0 DEFAULT_ENGINE) +endif() + +# Option to select the engine +set(ENGINE "${DEFAULT_ENGINE}" CACHE STRING "Select the engine to build with") +set_property(CACHE ENGINE PROPERTY STRINGS ${AVAILABLE_ENGINES}) + +# Validate selected engine +list(FIND AVAILABLE_ENGINES "${ENGINE}" ENGINE_INDEX) +if(ENGINE_INDEX EQUAL -1) + string(REPLACE ";" ", " ENGINES_STR "${AVAILABLE_ENGINES}") + message(FATAL_ERROR "Invalid ENGINE value: ${ENGINE}. Available engines: ${ENGINES_STR}") +endif() + +message(STATUS "Using engine: ${ENGINE}") + +# ------------------- +# Build Configuration +# ------------------- + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# Source files +set(SOURCES + "src/dllmain.cpp" + "src/utils.cpp" + "src/Framework/Member.cpp" + "src/Framework/Printer.cpp" + "src/Engine.cpp" +) + +# Header files +set(HEADERS + "include/version.hpp" + "include/dllmain.hpp" + "include/utils.hpp" + "include/Framework/Member.hpp" + "include/Framework/Printer.hpp" + "include/Engine.hpp" +) + +# Engine files +set(ENGINE_SOURCES + "engine/${ENGINE}/Configuration.cpp" + "engine/${ENGINE}/GameDefines.cpp" + "engine/${ENGINE}/PiecesOfCode.cpp" +) +set(ENGINE_HEADERS + "engine/${ENGINE}/Configuration.hpp" + "engine/${ENGINE}/GameDefines.hpp" + "engine/${ENGINE}/PiecesOfCode.hpp" +) + +# Organize source and header files into groups for IDEs +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/engine" + PREFIX "Engine Files" + FILES ${ENGINE_SOURCES} ${ENGINE_HEADERS}) +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/src" + PREFIX "Source Files" + FILES ${SOURCES}) +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/include" + PREFIX "Header Files" + FILES ${HEADERS}) + +# Create shared library (DLL) +add_library(${PROJECT_NAME} SHARED ${SOURCES} ${HEADERS} ${ENGINE_SOURCES} ${ENGINE_HEADERS}) + +# Define the selected engine +target_compile_definitions(${PROJECT_NAME} PRIVATE ENGINE=${ENGINE}) + +# Include directories +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/engine) + +# -------------------------- +# Compiler-Specific Settings +# -------------------------- + +# Compiler definitions +target_compile_definitions(${PROJECT_NAME} PRIVATE + $<$:_DEBUG> +) + +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # Compiler definitions + target_compile_definitions(${PROJECT_NAME} PRIVATE + $<$:_DEBUG> + _CONSOLE + _WINDLL + _UNICODE + UNICODE + ) + + # Compiler options + target_compile_options(${PROJECT_NAME} PRIVATE + /fp:precise # Precise floating-point model + /Gd # __cdecl calling convention + /GS # Buffer security checks + /Oy- # Disable frame pointer omission + /permissive- # Standards conformance + /sdl # SDL (Security Development Lifecycle) checks + /Zc:wchar_t # Treat wchar_t as a native type + /Zc:inline # Enforce inline semantics + /Zc:forScope # Enforce for-scope semantics + + # Enable optimizations + $<$:/GL> + $<$:/GL> + $<$:/GL> + + # Function-level linking + $<$:/Gy> + $<$:/Gy> + $<$:/Gy> + + # Intrinsic functions + $<$:/Oi> + $<$:/Oi> + ) + + # Linker definitions + target_link_options(${PROJECT_NAME} PRIVATE + /NXCOMPAT # Non-executable memory protection + /SUBSYSTEM:CONSOLE # Console subsystem + $<$:/SAFESEH> + + # Incremental linking + $<$:/LTCG:incremental> + $<$:/LTCG:incremental> + $<$:/LTCG:incremental> + + # Optimization settings + $<$:/OPT:REF> + $<$:/OPT:ICF> + $<$:/OPT:REF> + $<$:/OPT:ICF> + $<$:/OPT:REF> + $<$:/OPT:ICF> + ) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + # Compiler options + target_compile_options(${PROJECT_NAME} PRIVATE + -fms-extensions # Enable MS extensions + -fstack-protector-strong # Buffer security checks (equivalent to /GS) + -fno-omit-frame-pointer # Disable frame pointer omission (equivalent to /Oy-) + + # Enable optimizations for Release builds + $<$:-flto=auto> + $<$:-flto=auto> + $<$:-flto=auto> + + # Function-level linking (equivalent to /Gy) + $<$:-ffunction-sections> + $<$:-fdata-sections> + $<$:-ffunction-sections> + $<$:-fdata-sections> + $<$:-ffunction-sections> + $<$:-fdata-sections> + ) + + # Linker options + target_link_options(${PROJECT_NAME} PRIVATE + # Link-time optimization for Release builds + $<$:-flto=auto> + $<$:-flto=auto> + $<$:-flto=auto> + + # Dead code elimination (equivalent to /OPT:REF and /OPT:ICF) + $<$:-Wl,--gc-sections> + $<$:-Wl,--gc-sections> + $<$:-Wl,--gc-sections> + -fno-use-linker-plugin + ) + + #if MinGW, disable linker plugin usage + if(CMAKE_SYSTEM_NAME STREQUAL "Windows" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_link_libraries(${PROJECT_NAME} PRIVATE + -fno-use-linker-plugin + ) + endif() +else() + message(WARNING "Unknown compiler: ${CMAKE_CXX_COMPILER_ID}. Compiler-specific settings not applied.") +endif() + +# -------------------------- +# Platform-Specific Settings +# -------------------------- + +if(WIN32) + # Link against necessary system libraries + target_link_libraries(${PROJECT_NAME} PRIVATE + psapi + ) +elseif(UNIX) + # Link against necessary system libraries + target_link_libraries(${PROJECT_NAME} PRIVATE + pthread + ) +else() + message(WARNING "Unknown platform: ${CMAKE_SYSTEM_NAME}. Platform-specific settings not applied.") +endif() + +# -------------------- +# Output Configuration +# -------------------- + +# Set output directory +set_target_properties(${PROJECT_NAME} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib +) diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 0000000..de6465a --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,92 @@ +{ + "configurations": [ + { + "name": "x86-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x86" ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "x86-Release", + "generator": "Ninja", + "configurationType": "Release", + "inheritEnvironments": [ "msvc_x86" ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "x86-RelWithDebInfo", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ "msvc_x86" ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "x86-MinSizeRel", + "generator": "Ninja", + "configurationType": "MinSizeRel", + "inheritEnvironments": [ "msvc_x86" ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64" ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "x64-Release", + "generator": "Ninja", + "configurationType": "Release", + "inheritEnvironments": [ "msvc_x64" ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "x64-RelWithDebInfo", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "inheritEnvironments": [ "msvc_x64" ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + }, + { + "name": "x64-MinSizeRel", + "generator": "Ninja", + "configurationType": "MinSizeRel", + "inheritEnvironments": [ "msvc_x64" ], + "buildRoot": "${projectDir}\\build\\${name}", + "installRoot": "${projectDir}\\build\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "" + } + ] +} \ No newline at end of file diff --git a/CodeRedGenerator.sln b/CodeRedGenerator.sln deleted file mode 100644 index 9d83263..0000000 --- a/CodeRedGenerator.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.3.32929.385 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CodeRedGenerator", "CodeRedGenerator.vcxproj", "{A023646C-04F2-4DA2-8E90-7F7974D9B1F0}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A023646C-04F2-4DA2-8E90-7F7974D9B1F0}.Debug|x64.ActiveCfg = Debug|x64 - {A023646C-04F2-4DA2-8E90-7F7974D9B1F0}.Debug|x64.Build.0 = Debug|x64 - {A023646C-04F2-4DA2-8E90-7F7974D9B1F0}.Debug|x86.ActiveCfg = Debug|Win32 - {A023646C-04F2-4DA2-8E90-7F7974D9B1F0}.Debug|x86.Build.0 = Debug|Win32 - {A023646C-04F2-4DA2-8E90-7F7974D9B1F0}.Release|x64.ActiveCfg = Release|x64 - {A023646C-04F2-4DA2-8E90-7F7974D9B1F0}.Release|x64.Build.0 = Release|x64 - {A023646C-04F2-4DA2-8E90-7F7974D9B1F0}.Release|x86.ActiveCfg = Release|Win32 - {A023646C-04F2-4DA2-8E90-7F7974D9B1F0}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {9CCB137F-4105-4720-8D88-3D2E95DA465C} - EndGlobalSection -EndGlobal diff --git a/CodeRedGenerator.vcxproj b/CodeRedGenerator.vcxproj deleted file mode 100644 index 90f6aff..0000000 --- a/CodeRedGenerator.vcxproj +++ /dev/null @@ -1,166 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {a023646c-04f2-4da2-8e90-7f7974d9b1f0} - CodeRedGenerator - 10.0 - - - - DynamicLibrary - true - v143 - Unicode - - - DynamicLibrary - false - v143 - true - Unicode - - - DynamicLibrary - true - v143 - Unicode - - - DynamicLibrary - false - v143 - true - Unicode - - - - - - - - - - - - - - - - - - - - - - Level3 - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - stdc17 - - - Console - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - stdc17 - - - Console - true - true - true - - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - stdc17 - - - Console - true - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp20 - stdc17 - - - Console - true - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/CodeRedGenerator.vcxproj.filters b/CodeRedGenerator.vcxproj.filters deleted file mode 100644 index 4dccb35..0000000 --- a/CodeRedGenerator.vcxproj.filters +++ /dev/null @@ -1,94 +0,0 @@ - - - - - {648e9363-7c33-4996-9a3d-af04cd4ab306} - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {7d231ced-cda7-4c6d-a2fb-53b16a91b865} - - - {a20e86da-a02e-4059-b881-99b17e7ea477} - - - {b83839af-c722-41d1-8066-a179934937ed} - - - {3beb0f8b-1c3e-44fb-9ab8-bc9c7aa9dd90} - - - - - Engine - - - Engine\Template - - - Engine\Template - - - Engine\Template - - - Source Files - - - Source Files - - - Framework - - - Engine\Dishonered - - - Engine\Dishonered - - - Engine\Dishonered - - - Framework - - - - - Engine - - - Engine\Template - - - Engine\Template - - - Engine\Template - - - Header Files - - - Header Files - - - Framework - - - Engine\Dishonered - - - Engine\Dishonered - - - Engine\Dishonered - - - Framework - - - \ No newline at end of file diff --git a/CodeRedGenerator.vcxproj.user b/CodeRedGenerator.vcxproj.user deleted file mode 100644 index 88a5509..0000000 --- a/CodeRedGenerator.vcxproj.user +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Engine/Engine.hpp b/Engine/Engine.hpp deleted file mode 100644 index a78b05b..0000000 --- a/Engine/Engine.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -#include - -// Here is where you include your custom engine files, only "GameDefines.hpp" and "PiecesOfCode.hpp" is needed. -#include "Template/GameDefines.hpp" -#include "Template/PiecesOfCode.hpp" - -/* -# ========================================================================================= # -# Engine -# ========================================================================================= # -*/ - -// These are global variables for the generator, you do not need to change them; they are assigned in the cpp file. - -class GEngine -{ -private: - static std::string m_name; - static std::string m_version; - static std::string m_credits; - static std::string m_links; - -public: - static const std::string& GetName(); - static const std::string& GetVersion(); - static const std::string& GetCredits(); - static const std::string& GetLinks(); - -public: - GEngine() = delete; -}; - -/* -# ========================================================================================= # -# -# ========================================================================================= # -*/ \ No newline at end of file diff --git a/README.md b/README.md index b7fdf3c..4ee3ec3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## CodeRed Generator 3 (v1.1.7) +## CodeRed Generator 3 (v1.2.0) This is a C++20 Unreal Engine 3 SDK generator that was originally based off the source of [TheFeckless's UE3 SDK Generator](https://www.unknowncheats.me/forum/unreal-engine-3-a/71911-thefeckless-ue3-sdk-generator.html). It has since grown into its own project which utilizes C++20, strings, filesystem paths, and modern file streams; along with converting legacy UE3 features to more modern and user friendly ones while still being compatible with UE3. @@ -11,7 +11,7 @@ Final generated SDK is plug and play, just `#include "SdkHeaders.hpp"` in your p You have the option to generate an SDK using either offsets or patterns for GObjects and GNames. - **Class Alignment** -Full support for both x32 bit and x64 bit games, just change the `Alignment` value in `Configuration.cpp`. +Full support for both x32 bit and x64 bit games, just change the `Alignment` value in [Configuration.cpp](engine/Template/Configuration.cpp). - **Full Customization** You have full customization over the final generated SDK, use enum classes, remove native flags, class alignment, and even comment spacing. @@ -19,15 +19,16 @@ You have full customization over the final generated SDK, use enum classes, remo ### Requirements - ISO C++20 Standard. -- Visual Studio or another Windows based compiler (For Windows header files, along with the PSAPI library). +- CMake 3.20 or higher. +- A Windows-based compiler such as MSVC or MinGW (for Windows header files and the PSAPI library). ## Getting Started -Included in this project is a template folder located in `Engine/Template`, to get started copy and paste this folder and rename it to the game you would like to use. To generate from your newly created engine folder don't forget to change the includes in the `Engine.hpp` file. +Included in this project is a template folder located in [engine/Template](engine/Template), to get started copy and paste this folder and rename it to the game you would like to use. -In the `Configuration.hpp` file there are two defines, one is `NO_LOGGING` which disables writing to a log file, and another define called `UTF16`. If your game is using wide characters you will need to uncomment this define out, if not `UTF8` will be used by default. +In the [Configuration.hpp](engine/Template/Configuration.hpp) file there are two defines, one is `NO_LOGGING` which disables writing to a log file, and another define called `UTF16`. If your game is using wide characters you will need to uncomment this define out, if not `UTF8` will be used by default. -Any further configuration **MUST BE DONE IN THE `Configuration.cpp` ONLY!** This file contains everything from class alignment, process event settings, global patterns and offsets, your games name and version, and most importantly the directory used for SDK generation. +Any further configuration **MUST BE DONE IN THE [Configuration.cpp](engine/Template/Configuration.cpp) ONLY!** This file contains everything from class alignment, process event settings, global patterns and offsets, your games name and version, and most importantly the directory used for SDK generation. ![](https://i.imgur.com/q2tFJ7I.png) @@ -37,16 +38,22 @@ Any further configuration **MUST BE DONE IN THE `Configuration.cpp` ONLY!** This ## Generation -Once you have your custom engine folder setup, all that's left is to fill out your class and struct fields for your game. This generator uses a unique `REGISTER_MEMBER` macro to define class/struct members. This is very important for your final generated SDK as it's used to calculate offsets and unknown data all automatically, without needing to modify your `PiecesOfCode.cpp` unlike in Feckless's generator. +Once you have your custom engine folder setup, all that's left is to fill out your class and struct fields for your game. This generator uses a unique `REGISTER_MEMBER` macro to define class/struct members. This is very important for your final generated SDK as it's used to calculate offsets and unknown data all automatically, without needing to modify your [PiecesOfCode.cpp](engine/Template/PiecesOfCode.cpp) unlike in Feckless's generator. Any class/struct member outside of whats in the `EMemberTypes` does NOT need to be registered with the `REGISTER_MEMBER` macro, so feel free to place padding or buffers in between. ![](https://i.imgur.com/k9vawPv.png) -Once all your classes are filled out and you've made the necessary changes in `Configuration.cpp`, double check you didn't forget to set an out path in `Configuration.cpp` and have the right files included in `Engine.hpp`. After that just compile as a DLL and manually inject into your game, generation will start automatically and will prompt you when it is completed. +Once all your classes are filled out and you've made the necessary changes in [Configuration.cpp](engine/Template/Configuration.cpp), double check you didn't forget to set an output path. After that build the project and manually inject the DLL into your game. Generation will start automatically and will prompt you when it is completed. ## Changelog +### v1.2.0 +- Replaced Visual Studio solution with CMake for cross-compiler support. +- Restructured project directories: source files are now in [src/](src/), headers in [include/](include/), and engine configurations in [engine/](engine/). +- Removed precompiled headers (PCH) for simpler builds. +- CMake auto-detects available engines and allows selection via `-DENGINE=`. + ### v1.1.7 - Removed the "UnrealProperty::CantMemcpy" function in favor of the new one "UnrealProperty::ShouldMemcpy()". - Fixed there being a trailing semicolon generated in the "GenerateFunctionCode" function. @@ -61,8 +68,8 @@ Once all your classes are filled out and you've made the necessary changes in `C - Moved the "CreateValidName" and "CreateWindowsFunction" functions inside of the "UnrealObject" class. - Fixed the comment spacing for classes using the struct spacing instead of class spacing from your config file. -### v1.1.4 -- Added a new engine folder for Dishonered, which contains all needed fields to generate an sdk for that game. +### v1.1.4 +- Added a new engine folder for Dishonored, which contains all needed fields to generate an sdk for that game. - Added extra safety checks for the string helper functions in "Printer.hpp/cpp". - Fixed the object flag values in "GameDefines.hpp" and also "PiecesOfCodes.cpp". - Updated the "FindClass" and "FindFunction" functions slightly. @@ -149,4 +156,4 @@ Once all your classes are filled out and you've made the necessary changes in `C - Decided to replace the "ZeroMemory" macro with use memset instead, "ZeroMemory" calls this anyway. ### v1.0.1 -- Added an include for "Windows.h" in the generated "GameDefines.hpp" file, this is needed for the "ZeroMemory" maco. \ No newline at end of file +- Added an include for "Windows.h" in the generated "GameDefines.hpp" file, this is needed for the "ZeroMemory" maco. diff --git a/Engine/Dishonered/Configuration.cpp b/engine/Dishonored/Configuration.cpp similarity index 99% rename from Engine/Dishonered/Configuration.cpp rename to engine/Dishonored/Configuration.cpp index 0ef2221..f5c6155 100644 --- a/Engine/Dishonered/Configuration.cpp +++ b/engine/Dishonored/Configuration.cpp @@ -1,6 +1,8 @@ #include "Configuration.hpp" #include "PiecesOfCode.hpp" +#include + /* # ========================================================================================= # # Cosmetics @@ -285,7 +287,7 @@ const std::string& GConfig::GetGNameMask() */ // Mainly just used for the printed headers at the top of each generated file. -std::string GConfig::m_gameNameLong = "Dishonered"; +std::string GConfig::m_gameNameLong = "Dishonored"; // This is used for the output folder name, along with the printed headers at the top of each file. std::string GConfig::m_gameNameShort = "DSDK"; diff --git a/Engine/Dishonered/Configuration.hpp b/engine/Dishonored/Configuration.hpp similarity index 99% rename from Engine/Dishonered/Configuration.hpp rename to engine/Dishonored/Configuration.hpp index d694b88..2835048 100644 --- a/Engine/Dishonered/Configuration.hpp +++ b/engine/Dishonored/Configuration.hpp @@ -1,6 +1,8 @@ #pragma once -#include + #include +#include +#include #include /* diff --git a/Engine/Dishonered/GameDefines.cpp b/engine/Dishonored/GameDefines.cpp similarity index 100% rename from Engine/Dishonered/GameDefines.cpp rename to engine/Dishonored/GameDefines.cpp diff --git a/Engine/Dishonered/GameDefines.hpp b/engine/Dishonored/GameDefines.hpp similarity index 99% rename from Engine/Dishonered/GameDefines.hpp rename to engine/Dishonored/GameDefines.hpp index cea3c13..c49dff6 100644 --- a/Engine/Dishonered/GameDefines.hpp +++ b/engine/Dishonored/GameDefines.hpp @@ -1,16 +1,12 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include + #include "Configuration.hpp" -#include "../../Framework/Member.hpp" +#include + +#include + +#include +#include /* # ========================================================================================= # @@ -540,6 +536,13 @@ class TArray } }; +// FPointer +// (0x0000 - 0x0004) +struct FPointer +{ + uintptr_t Dummy; // 0x0000 (0x04) +}; + // THIS CLASS CAN BE GAME SPECIFIC, MOST GAMES WILL GENERATE A STRUCT MIRROR! template class TMap @@ -573,12 +576,12 @@ class TMap public: TMap() : - IndirectData(NULL), + IndirectData(0), NumBits(0), MaxBits(0), FirstFreeIndex(0), NumFreeIndices(0), - Hash(nullptr), + Hash(0), InlineHash(0), HashSize(0) { @@ -586,12 +589,12 @@ class TMap } TMap(struct FMap_Mirror& other) : - IndirectData(NULL), + IndirectData(0), NumBits(0), MaxBits(0), FirstFreeIndex(0), NumFreeIndices(0), - Hash(nullptr), + Hash(0), InlineHash(0), HashSize(0) { @@ -599,12 +602,12 @@ class TMap } TMap(const TMap& other) : - IndirectData(NULL), + IndirectData(0), NumBits(0), MaxBits(0), FirstFreeIndex(0), NumFreeIndices(0), - Hash(nullptr), + Hash(0), InlineHash(0), HashSize(0) { @@ -1099,13 +1102,6 @@ struct FScriptDelegate class FName FunctionName; // 0x0004 (0x08) }; -// FPointer -// (0x0000 - 0x0004) -struct FPointer -{ - uintptr_t Dummy; // 0x0000 (0x04) -}; - // FQWord // (0x0000 - 0x0008) struct FQWord diff --git a/Engine/Dishonered/PiecesOfCode.cpp b/engine/Dishonored/PiecesOfCode.cpp similarity index 99% rename from Engine/Dishonered/PiecesOfCode.cpp rename to engine/Dishonored/PiecesOfCode.cpp index 99a7099..abcd486 100644 --- a/Engine/Dishonered/PiecesOfCode.cpp +++ b/engine/Dishonored/PiecesOfCode.cpp @@ -271,7 +271,7 @@ namespace PiecesOfCode "\t\tMaxBits(0),\n" "\t\tFirstFreeIndex(0),\n" "\t\tNumFreeIndices(0),\n" - "\t\tHash(nullptr),\n" + "\t\tHash(NULL),\n" "\t\tInlineHash(0),\n" "\t\tHashSize(0)\n" "\t{\n" @@ -284,7 +284,7 @@ namespace PiecesOfCode "\t\tMaxBits(0),\n" "\t\tFirstFreeIndex(0),\n" "\t\tNumFreeIndices(0),\n" - "\t\tHash(nullptr),\n" + "\t\tHash(NULL),\n" "\t\tInlineHash(0),\n" "\t\tHashSize(0)\n" "\t{\n" @@ -297,7 +297,7 @@ namespace PiecesOfCode "\t\tMaxBits(0),\n" "\t\tFirstFreeIndex(0),\n" "\t\tNumFreeIndices(0),\n" - "\t\tHash(nullptr),\n" + "\t\tHash(NULL),\n" "\t\tInlineHash(0),\n" "\t\tHashSize(0)\n" "\t{\n" diff --git a/Engine/Dishonered/PiecesOfCode.hpp b/engine/Dishonored/PiecesOfCode.hpp similarity index 99% rename from Engine/Dishonered/PiecesOfCode.hpp rename to engine/Dishonored/PiecesOfCode.hpp index 45f63e7..d67d300 100644 --- a/Engine/Dishonered/PiecesOfCode.hpp +++ b/engine/Dishonored/PiecesOfCode.hpp @@ -1,4 +1,5 @@ #pragma once + #include /* diff --git a/Engine/Template/Configuration.cpp b/engine/Template/Configuration.cpp similarity index 99% rename from Engine/Template/Configuration.cpp rename to engine/Template/Configuration.cpp index bdf49b7..8cd98f7 100644 --- a/Engine/Template/Configuration.cpp +++ b/engine/Template/Configuration.cpp @@ -1,6 +1,8 @@ #include "Configuration.hpp" #include "PiecesOfCode.hpp" +#include + /* # ========================================================================================= # # Cosmetics diff --git a/Engine/Template/Configuration.hpp b/engine/Template/Configuration.hpp similarity index 99% rename from Engine/Template/Configuration.hpp rename to engine/Template/Configuration.hpp index cc6de19..e752ab4 100644 --- a/Engine/Template/Configuration.hpp +++ b/engine/Template/Configuration.hpp @@ -1,6 +1,8 @@ #pragma once -#include + #include +#include +#include #include /* diff --git a/Engine/Template/GameDefines.cpp b/engine/Template/GameDefines.cpp similarity index 100% rename from Engine/Template/GameDefines.cpp rename to engine/Template/GameDefines.cpp diff --git a/Engine/Template/GameDefines.hpp b/engine/Template/GameDefines.hpp similarity index 99% rename from Engine/Template/GameDefines.hpp rename to engine/Template/GameDefines.hpp index f7c5353..1c692fa 100644 --- a/Engine/Template/GameDefines.hpp +++ b/engine/Template/GameDefines.hpp @@ -1,16 +1,14 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include + #include "Configuration.hpp" -#include "../../Framework/Member.hpp" +#include + +#include +#include +#include + +#include +#include /* # ========================================================================================= # @@ -541,6 +539,13 @@ class TArray } }; +// FPointer +// (0x0000 - 0x0004) +struct FPointer +{ + uintptr_t Dummy; // 0x0000 (0x04) +}; + // THIS CLASS CAN BE GAME SPECIFIC, MOST GAMES WILL GENERATE A STRUCT MIRROR! template class TMap @@ -574,39 +579,39 @@ class TMap public: TMap() : - IndirectData(NULL), + IndirectData(0), NumBits(0), MaxBits(0), FirstFreeIndex(0), NumFreeIndices(0), InlineHash(0), - Hash(nullptr), + Hash(NULL), HashCount(0) { } TMap(struct FMap_Mirror& other) : - IndirectData(NULL), + IndirectData(0), NumBits(0), MaxBits(0), FirstFreeIndex(0), NumFreeIndices(0), InlineHash(0), - Hash(nullptr), + Hash(NULL), HashCount(0) { assign(other); } TMap(const TMap& other) : - IndirectData(NULL), + IndirectData(0), NumBits(0), MaxBits(0), FirstFreeIndex(0), NumFreeIndices(0), InlineHash(0), - Hash(nullptr), + Hash(NULL), HashCount(0) { assign(other); @@ -1100,13 +1105,6 @@ struct FScriptDelegate class FName FunctionName; // 0x0004 (0x08) }; -// FPointer -// (0x0000 - 0x0004) -struct FPointer -{ - uintptr_t Dummy; // 0x0000 (0x04) -}; - // FQWord // (0x0000 - 0x0008) struct FQWord diff --git a/Engine/Template/PiecesOfCode.cpp b/engine/Template/PiecesOfCode.cpp similarity index 99% rename from Engine/Template/PiecesOfCode.cpp rename to engine/Template/PiecesOfCode.cpp index 8e889d6..40e82cc 100644 --- a/Engine/Template/PiecesOfCode.cpp +++ b/engine/Template/PiecesOfCode.cpp @@ -271,7 +271,7 @@ namespace PiecesOfCode "\t\tFirstFreeIndex(0),\n" "\t\tNumFreeIndices(0),\n" "\t\tInlineHash(0),\n" - "\t\tHash(nullptr),\n" + "\t\tHash(NULL),\n" "\t\tHashCount(0)\n" "\t{\n" "\n" @@ -284,7 +284,7 @@ namespace PiecesOfCode "\t\tFirstFreeIndex(0),\n" "\t\tNumFreeIndices(0),\n" "\t\tInlineHash(0),\n" - "\t\tHash(nullptr),\n" + "\t\tHash(NULL),\n" "\t\tHashCount(0)\n" "\t{\n" "\t\tassign(other);\n" @@ -297,7 +297,7 @@ namespace PiecesOfCode "\t\tFirstFreeIndex(0),\n" "\t\tNumFreeIndices(0),\n" "\t\tInlineHash(0),\n" - "\t\tHash(nullptr),\n" + "\t\tHash(NULL),\n" "\t\tHashCount(0)\n" "\t{\n" "\t\tassign(other);\n" diff --git a/Engine/Template/PiecesOfCode.hpp b/engine/Template/PiecesOfCode.hpp similarity index 99% rename from Engine/Template/PiecesOfCode.hpp rename to engine/Template/PiecesOfCode.hpp index 45f63e7..d67d300 100644 --- a/Engine/Template/PiecesOfCode.hpp +++ b/engine/Template/PiecesOfCode.hpp @@ -1,4 +1,5 @@ #pragma once + #include /* diff --git a/include/Engine.hpp b/include/Engine.hpp new file mode 100644 index 0000000..8fcc199 --- /dev/null +++ b/include/Engine.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include + +// Preprocessor helpers for computed includes +#define STRINGIFY_IMPL(x) #x +#define STRINGIFY(x) STRINGIFY_IMPL(x) +#define ENGINE_INCLUDE(file) STRINGIFY(ENGINE/file) + +// Engine files are included based on build tool's "ENGINE" option (e.g., -DENGINE=Template), only "GameDefines.hpp" and "PiecesOfCode.hpp" is needed. +#include ENGINE_INCLUDE(GameDefines.hpp) +#include ENGINE_INCLUDE(PiecesOfCode.hpp) + +/* +# ========================================================================================= # +# Engine +# ========================================================================================= # +*/ + +// These are global variables for the generator, you do not need to change them; they are assigned in the cpp file. + +class GEngine +{ +private: + static std::string_view m_name; + static std::string_view m_version; + static std::string_view m_credits; + static std::string_view m_links; + +public: + static const std::string_view& GetName(); + static const std::string_view& GetVersion(); + static const std::string_view& GetCredits(); + static const std::string_view& GetLinks(); + +public: + GEngine() = delete; +}; + +/* +# ========================================================================================= # +# +# ========================================================================================= # +*/ \ No newline at end of file diff --git a/Framework/Member.hpp b/include/Framework/Member.hpp similarity index 99% rename from Framework/Member.hpp rename to include/Framework/Member.hpp index ddd3fac..2737055 100644 --- a/Framework/Member.hpp +++ b/include/Framework/Member.hpp @@ -1,4 +1,7 @@ #pragma once + +#include + #include #include #include diff --git a/Framework/Printer.hpp b/include/Framework/Printer.hpp similarity index 96% rename from Framework/Printer.hpp rename to include/Framework/Printer.hpp index 1680f43..62d6c3f 100644 --- a/Framework/Printer.hpp +++ b/include/Framework/Printer.hpp @@ -1,10 +1,10 @@ #pragma once -#include -#include + +#include + #include #include #include -#include #include enum class EWidthTypes : uint32_t diff --git a/dllmain.hpp b/include/dllmain.hpp similarity index 95% rename from dllmain.hpp rename to include/dllmain.hpp index 8288551..a8c23cf 100644 --- a/dllmain.hpp +++ b/include/dllmain.hpp @@ -1,7 +1,7 @@ #pragma once -#include "pch.hpp" -#include "Framework/Printer.hpp" -#include "Engine/Engine.hpp" + +#include +#include class UnrealObject { @@ -177,9 +177,9 @@ class GLogger namespace Utils { - void MessageboxInfo(const std::string& message); - void MessageboxWarn(const std::string& message); - void MessageboxError(const std::string& message); + void MessageBoxInfo(const std::string_view& message); + void MessageBoxWarn(const std::string_view& message); + void MessageBoxError(const std::string_view& message); bool SortProperty(const UnrealProperty& unrealPropA, const UnrealProperty& unrealPropB); bool SortPropertyPair(const std::pair& pairA, const std::pair& pairB); @@ -193,7 +193,7 @@ namespace Retrievers uintptr_t GetBaseAddress(); uintptr_t GetOffset(void* pointer); - uintptr_t FindPattern(const uint8_t* pattern, const std::string& mask); + uintptr_t FindPattern(const std::basic_string_view& pattern, const std::string_view& mask); } namespace ConstGenerator diff --git a/include/utils.hpp b/include/utils.hpp new file mode 100644 index 0000000..da97932 --- /dev/null +++ b/include/utils.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +namespace Utils +{ + void MessageBoxInfo(const std::string_view& message); + void MessageBoxWarn(const std::string_view& message); + void MessageBoxError(const std::string_view& message); +} + +namespace Retrievers +{ + uintptr_t GetBaseAddress(); + uintptr_t FindPattern(const std::basic_string_view& pattern, const std::string_view& mask); +} diff --git a/pch.cpp b/pch.cpp deleted file mode 100644 index 7f3196a..0000000 --- a/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.hpp" \ No newline at end of file diff --git a/pch.hpp b/pch.hpp deleted file mode 100644 index 0bbf0b7..0000000 --- a/pch.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#pragma comment(lib, "Psapi.lib") \ No newline at end of file diff --git a/Engine/Engine.cpp b/src/Engine.cpp similarity index 52% rename from Engine/Engine.cpp rename to src/Engine.cpp index d72eb80..ec037d5 100644 --- a/Engine/Engine.cpp +++ b/src/Engine.cpp @@ -1,4 +1,6 @@ -#include "Engine.hpp" +#include + +#include /* # ========================================================================================= # @@ -6,30 +8,27 @@ # ========================================================================================= # */ -std::string GEngine::m_name = "CodeRedGenerator"; - -std::string GEngine::m_version = "v1.1.7"; - -std::string GEngine::m_credits = "ItsBranK, TheFeckless"; - -std::string GEngine::m_links = "www.github.com/CodeRedModding/CodeRed-Generator"; +std::string_view GEngine::m_name = PROJECT_NAME; +std::string_view GEngine::m_version = PROJECT_VERSION; +std::string_view GEngine::m_credits = PROJECT_AUTHORS; +std::string_view GEngine::m_links = PROJECT_HOMEPAGE_URL; -const std::string& GEngine::GetName() +const std::string_view& GEngine::GetName() { return m_name; } -const std::string& GEngine::GetVersion() +const std::string_view& GEngine::GetVersion() { return m_version; } -const std::string& GEngine::GetCredits() +const std::string_view& GEngine::GetCredits() { return m_credits; } -const std::string& GEngine::GetLinks() +const std::string_view& GEngine::GetLinks() { return m_links; } diff --git a/Framework/Member.cpp b/src/Framework/Member.cpp similarity index 99% rename from Framework/Member.cpp rename to src/Framework/Member.cpp index ce3b1ca..ccb0285 100644 --- a/Framework/Member.cpp +++ b/src/Framework/Member.cpp @@ -1,4 +1,4 @@ -#include "../Engine/Engine.hpp" +#include /* # ========================================================================================= # diff --git a/Framework/Printer.cpp b/src/Framework/Printer.cpp similarity index 97% rename from Framework/Printer.cpp rename to src/Framework/Printer.cpp index 4484916..87cd1cf 100644 --- a/Framework/Printer.cpp +++ b/src/Framework/Printer.cpp @@ -1,5 +1,5 @@ -#include "Printer.hpp" -#include "../Engine/Engine.hpp" +#include +#include namespace Printer { @@ -70,7 +70,7 @@ namespace Printer while (replacePos != std::string::npos) { - baseStr.replace(replacePos, strToReplace.length(), replaceWithStr); + baseStr.replace(replacePos, replaceLength, replaceWithStr); replacePos = baseStr.find(strToReplace); } } diff --git a/dllmain.cpp b/src/dllmain.cpp similarity index 98% rename from dllmain.cpp rename to src/dllmain.cpp index 482415d..dc435aa 100644 --- a/dllmain.cpp +++ b/src/dllmain.cpp @@ -1,4 +1,6 @@ -#include "dllmain.hpp" +#include + +#include static constexpr uint32_t UNKNOWN_DATA_SPACING = 2; static constexpr uint32_t LOG_FILE_SPACING = 75; @@ -956,12 +958,12 @@ bool GLogger::Open() if (std::filesystem::exists(fullDirectory)) { - m_file.open(fullDirectory / (GEngine::GetName() + ".log")); + m_file.open(fullDirectory / (std::format("{}.log", GEngine::GetName()))); return true; } else { - Utils::MessageboxError("Error: Failed to create the log file, might not have the right permissions or your directory is invalid!"); + Utils::MessageBoxError("Error: Failed to create the log file, might not have the right permissions or your directory is invalid!"); } } #endif @@ -1050,34 +1052,6 @@ void GLogger::LogStructPadding(class UScriptStruct* uScriptStruct, size_t paddin namespace Utils { - void MessageboxExt(const std::string& message, uint32_t flags) - { -#ifdef _WIN32 - MessageBoxA(NULL, message.c_str(), GEngine::GetName().c_str(), flags); -#endif - } - - void MessageboxInfo(const std::string& message) - { -#ifdef _WIN32 - MessageboxExt(message, (MB_OK | MB_ICONINFORMATION)); -#endif - } - - void MessageboxWarn(const std::string& message) - { -#ifdef _WIN32 - MessageboxExt(message, (MB_OK | MB_ICONWARNING)); -#endif - } - - void MessageboxError(const std::string& message) - { -#ifdef _WIN32 - MessageboxExt(message, (MB_OK | MB_ICONERROR)); -#endif - } - bool SortProperty(const UnrealProperty& unrealPropA, const UnrealProperty& unrealPropB) { if (unrealPropA.Property && unrealPropB.Property) @@ -1267,11 +1241,6 @@ namespace Retrievers } } - uintptr_t GetBaseAddress() - { - return reinterpret_cast(GetModuleHandle(NULL)); - } - uintptr_t GetOffset(void* pointer) { uintptr_t baseAddress = GetBaseAddress(); @@ -1282,45 +1251,7 @@ namespace Retrievers return (address - baseAddress); } - return NULL; - } - - uintptr_t FindPattern(const uint8_t* pattern, const std::string& mask) - { - if (pattern && !mask.empty()) - { - MODULEINFO miInfos; - ZeroMemory(&miInfos, sizeof(MODULEINFO)); - - HMODULE hModule = GetModuleHandle(NULL); - K32GetModuleInformation(GetCurrentProcess(), hModule, &miInfos, sizeof(MODULEINFO)); - - uintptr_t start = reinterpret_cast(hModule); - uintptr_t end = (start + miInfos.SizeOfImage); - - size_t currentPos = 0; - size_t maskLength = (mask.length() - 1); - - for (uintptr_t retAddress = start; retAddress < end; retAddress++) - { - if (*reinterpret_cast(retAddress) == pattern[currentPos] || mask[currentPos] == '?') - { - if (currentPos == maskLength) - { - return (retAddress - maskLength); - } - - currentPos++; - } - else - { - retAddress -= currentPos; - currentPos = 0; - } - } - } - - return NULL; + return 0; } } @@ -1613,7 +1544,7 @@ namespace StructGenerator #ifndef NO_LOGGING GLogger::Log("Error: No registered members found for struct type \"" + Member::GetName(structType) + "\"!"); #endif - Utils::MessageboxError("Error: No registered members found for struct type \"" + Member::GetName(structType) + "\"!"); + Utils::MessageBoxError("Error: No registered members found for struct type \"" + Member::GetName(structType) + "\"!"); } } } @@ -2117,7 +2048,7 @@ namespace ClassGenerator #ifndef NO_LOGGING GLogger::LogClassSize(uClass, localSize); #endif - Utils::MessageboxError("Error: Incorrect class size detected for \"" + Member::GetName(classType) + "\", check the log file for more details!"); + Utils::MessageBoxError("Error: Incorrect class size detected for \"" + Member::GetName(classType) + "\", check the log file for more details!"); } } else @@ -2125,7 +2056,7 @@ namespace ClassGenerator #ifndef NO_LOGGING GLogger::Log("Error: No registered members found for class \"" + uClass->GetName() + "\"!"); #endif - Utils::MessageboxError("Error: No registered members found for \"" + Member::GetName(classType) + "\"!"); + Utils::MessageBoxError("Error: No registered members found for \"" + Member::GetName(classType) + "\"!"); } } } @@ -2712,7 +2643,7 @@ namespace FunctionGenerator else { stream << "\n\t// FIX PROCESSEVENT IN CONFIGURATION.CPP, INVALID INDEX"; - Utils::MessageboxWarn("Warning: ProcessEvent is not configured correctly in \"Configuration.cpp\", you set \"UsingIndex\" to true yet you did not provide a valid index for process event!"); + Utils::MessageBoxWarn("Warning: ProcessEvent is not configured correctly in \"Configuration.cpp\", you set \"UsingIndex\" to true yet you did not provide a valid index for process event!"); } if (processEventAddress) @@ -2739,7 +2670,7 @@ namespace FunctionGenerator else { stream << "\n\t// FIX PROCESSEVENT IN CONFIGURATION.CPP, INVALID ADDRESS"; - Utils::MessageboxWarn("Warning: ProcessEvent is not configured correctly in \"Configuration.cpp\", failed to find a valid address!"); + Utils::MessageBoxWarn("Warning: ProcessEvent is not configured correctly in \"Configuration.cpp\", failed to find a valid address!"); } } @@ -3490,7 +3421,7 @@ namespace Generator } else { - Utils::MessageboxError("Failed locate the given directory, cannot generate an SDK!"); + Utils::MessageBoxError("Failed locate the given directory, cannot generate an SDK!"); } } @@ -3507,7 +3438,7 @@ namespace Generator { if (Initialize(true)) { - Utils::MessageboxInfo("SDK generation has started, do not close the game until prompted to do so!"); + Utils::MessageBoxInfo("SDK generation has started, do not close the game until prompted to do so!"); std::chrono::time_point startTime = std::chrono::system_clock::now(); ProcessPackages(headerDirectory); @@ -3524,12 +3455,12 @@ namespace Generator GLogger::Close(); #endif - Utils::MessageboxInfo("SDK generation complete, finished in " + formattedTime + " seconds!"); + Utils::MessageBoxInfo("SDK generation complete, finished in " + formattedTime + " seconds!"); } } else { - Utils::MessageboxError("Failed to create the desired directory, cannot generate an SDK!"); + Utils::MessageBoxError("Failed to create the desired directory, cannot generate an SDK!"); } } @@ -3537,7 +3468,7 @@ namespace Generator { if (!GConfig::HasOutputPath()) { - Utils::MessageboxError("Looks like you forgot to set an output path for the generator! Please edit the output path in \"Configuration.cpp\" and recompile."); + Utils::MessageBoxError("Looks like you forgot to set an output path for the generator! Please edit the output path in \"Configuration.cpp\" and recompile."); return false; } @@ -3619,7 +3550,7 @@ namespace Generator } else { - Utils::MessageboxError("Failed to validate GObject & GNames, please make sure you have them configured properly in \"Configuration.cpp\"!"); + Utils::MessageBoxError("Failed to validate GObject & GNames, please make sure you have them configured properly in \"Configuration.cpp\"!"); return false; } } @@ -3654,7 +3585,7 @@ namespace Generator DumpGObjects(); } - Utils::MessageboxInfo("Finished dumping instances!"); + Utils::MessageBoxInfo("Finished dumping instances!"); } } @@ -3783,19 +3714,34 @@ namespace Generator } } -void OnAttach(HMODULE hModule) +void Start() { - DisableThreadLibraryCalls(hModule); + Utils::MessageBoxInfo("Ready to generate SDK!"); Generator::GenerateSDK(); Generator::DumpInstances(true, true); } -BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +#ifdef _WIN32 + +#include + +DWORD OnAttach(HMODULE hModule) +{ + DisableThreadLibraryCalls(hModule); + + Start(); + + FreeLibraryAndExitThread(hModule, 0); + return 0; +} + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) { - switch (ul_reason_for_call) + (void)lpReserved; + switch (dwReason) { case DLL_PROCESS_ATTACH: - CreateThread(nullptr, 0, reinterpret_cast(OnAttach), nullptr, 0, nullptr); + CreateThread(nullptr, 0, reinterpret_cast(OnAttach), hModule, 0, nullptr); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: @@ -3803,4 +3749,30 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv break; } return TRUE; -} \ No newline at end of file +} + +#elif __linux__ + +#include + +void* OnAttach(void* arg) +{ + (void)arg; + + Start(); + + pthread_exit(nullptr); + return nullptr; +} + +__attribute__((constructor)) void DllMain() +{ + pthread_t thread; + pthread_create(&thread, nullptr, OnAttach, nullptr); +} + +#else + +#error Unsupported platform! + +#endif diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..0d2fa59 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,325 @@ +#include + +#include + +#ifdef _WIN32 + +#include +#include + +namespace Utils +{ + void MessageBoxExt(const std::string_view& message, uint32_t flags) + { + MessageBoxA(NULL, message.data(), GEngine::GetName().data(), flags); + } + + void MessageBoxInfo(const std::string_view& message) + { + MessageBoxExt(message, (MB_OK | MB_ICONINFORMATION)); + } + + void MessageBoxWarn(const std::string_view& message) + { + MessageBoxExt(message, (MB_OK | MB_ICONWARNING)); + } + + void MessageBoxError(const std::string_view& message) + { + MessageBoxExt(message, (MB_OK | MB_ICONERROR)); + } +} + +namespace Retrievers +{ + uintptr_t GetBaseAddress() + { + return reinterpret_cast(GetModuleHandle(NULL)); + } + + uintptr_t FindPattern(const std::basic_string_view& pattern, const std::string_view& mask) + { + if (!pattern.empty() && !mask.empty()) + { + MODULEINFO miInfos; + ZeroMemory(&miInfos, sizeof(MODULEINFO)); + + HMODULE hModule = GetModuleHandle(NULL); + K32GetModuleInformation(GetCurrentProcess(), hModule, &miInfos, sizeof(MODULEINFO)); + + uintptr_t start = reinterpret_cast(hModule); + uintptr_t end = (start + miInfos.SizeOfImage); + + size_t currentPos = 0; + size_t maskLength = (mask.length() - 1); + + for (uintptr_t retAddress = start; retAddress < end; retAddress++) + { + if (*reinterpret_cast(retAddress) == pattern[currentPos] || mask[currentPos] == '?') + { + if (currentPos == maskLength) + { + return (retAddress - maskLength); + } + + currentPos++; + } + else + { + retAddress -= currentPos; + currentPos = 0; + } + } + } + + return 0; + } +} + +#elif __linux__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Utils +{ + namespace + { + enum MessageBoxLevel : uint8_t + { + Info = 0, + Warn = 1, + Error = 2, + }; + + bool TryExec(const std::vector& args) + { + pid_t pid = fork(); + + if (pid == -1) + { + return false; + } + + if (pid == 0) + { + // Child process: redirect stderr to /dev/null + int devnull = open("/dev/null", O_WRONLY); + if (devnull != -1) + { + dup2(devnull, STDERR_FILENO); + close(devnull); + } + + execvp(args[0], const_cast(args.data())); + _exit(127); + } + + // Parent process: wait for child + int status; + waitpid(pid, &status, 0); + + return WIFEXITED(status) && WEXITSTATUS(status) == 0; + } + } + + void MessageBoxExt(const std::string_view& message, MessageBoxLevel level) + { + std::string_view title(GEngine::GetName()); + std::string_view msg(message); + + // Blocking dialog commands (ordered by preference) + std::array commands = { + std::vector{ + "kdialog", std::array{"--msgbox", "--sorry", "--error"}[level], + msg.data(), "--title", title.data(), nullptr + }, + std::vector{ + "zenity", std::array{"--info", "--warning", "--error"}[level], + "--title", title.data(), "--text", msg.data(), nullptr + }, + std::vector{ + "notify-send", "-t", "0", "-w", + "-u", std::array{"normal", "critical", "critical"}[level], + "-a", title.data(), msg.data(), nullptr + }, + std::vector{ + "xmessage", "-buttons", "OK:0", + "-title", title.data(), msg.data(), nullptr + } + }; + + for (const auto& args : commands) + { + if (TryExec(args)) + return; + } + + // Fallback to stderr + std::cerr << "[" << title << "] " << msg << std::endl; + } + + void MessageBoxInfo(const std::string_view& message) + { + MessageBoxExt(message, MessageBoxLevel::Info); + } + + void MessageBoxWarn(const std::string_view& message) + { + MessageBoxExt(message, MessageBoxLevel::Warn); + } + + void MessageBoxError(const std::string_view& message) + { + MessageBoxExt(message, MessageBoxLevel::Error); + } +} + +namespace Retrievers +{ + namespace + { + struct ProcMapsEntry + { + ProcMapsEntry(std::string& line) + { + parse(line); + } + + uintptr_t start; + uintptr_t end; + struct + { + bool r; + bool w; + bool x; + bool s; + } perms; + uintptr_t offset; + struct + { + uint16_t major; + uint16_t minor; + } dev; + uint64_t inode; + std::string pathname; + + void parse(std::string& line) + { + this->pathname.resize(4096, '\0'); + char perms[4] = {0}; + sscanf(line.data(), "%tx-%tx %4c %tx %02hx:%02hx %lu %s", + &this->start, &this->end, + perms, &this->offset, + &this->dev.major, &this->dev.minor, + &this->inode, this->pathname.data()); + this->parse_perms(perms); + this->pathname.shrink_to_fit(); + } + + private: + void parse_perms(const char perms[4]) + { + this->perms.r = (perms[0] == 'r'); + this->perms.w = (perms[1] == 'w'); + this->perms.x = (perms[2] == 'x'); + this->perms.s = (perms[3] == 's' || perms[3] == 'S'); + } + }; + } + + uintptr_t GetBaseAddress() + { + std::ifstream input("/proc/self/maps"); + + for (std::string line; std::getline(input, line);) { + // Parse the proc_pid_maps line + ProcMapsEntry entry(line); + + // Check if this line corresponds to the main executable + if (entry.inode != 0 && std::filesystem::equivalent(entry.pathname, "/proc/self/exe")) { + return entry.start; + } + } + + assert(false && "Failed to find base address from /proc/self/maps"); + + return 0; + } + + uintptr_t FindPattern(const std::basic_string_view& pattern, const std::string_view& mask) + { + if (pattern.empty() || mask.empty()) + { + return 0; + } + + std::ifstream input("/proc/self/maps"); + uintptr_t start = 0; + uintptr_t end = 0; + + // Find the executable's memory range + for (std::string line; std::getline(input, line);) + { + ProcMapsEntry entry(line); + + // Only scan readable segments of the main executable + if (entry.inode != 0 && entry.perms.r && + std::filesystem::equivalent(entry.pathname, "/proc/self/exe")) + { + if (start == 0 || entry.start < start) + start = entry.start; + if (entry.end > end) + end = entry.end; + } + } + + if (start == 0 || end == 0) + { + return 0; + } + + // Pattern matching (same algorithm as Windows) + size_t currentPos = 0; + size_t maskLength = (mask.length() - 1); + + for (uintptr_t retAddress = start; retAddress < end; retAddress++) + { + if (*reinterpret_cast(retAddress) == pattern[currentPos] || mask[currentPos] == '?') + { + if (currentPos == maskLength) + { + return (retAddress - maskLength); + } + + currentPos++; + } + else + { + retAddress -= currentPos; + currentPos = 0; + } + } + + return 0; + } +} + +#else + +#error "Unsupported platform" + +#endif diff --git a/version.hpp.in b/version.hpp.in new file mode 100644 index 0000000..d15409f --- /dev/null +++ b/version.hpp.in @@ -0,0 +1,6 @@ +#pragma once + +#define PROJECT_NAME "@PROJECT_NAME@" +#define PROJECT_VERSION "@PROJECT_VERSION@" +#define PROJECT_AUTHORS "ItsBranK, TheFeckless" +#define PROJECT_HOMEPAGE_URL "@PROJECT_HOMEPAGE_URL@"