We rise from the ashes

This commit is contained in:
CallowBlack
2022-05-17 03:13:41 +03:00
parent 759118f830
commit 9999f2f9cb
832 changed files with 173179 additions and 8288 deletions

View File

@ -0,0 +1,282 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release_WS|x64">
<Configuration>Release_WS</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{adb35022-823b-4dc0-b495-3efefbd3a82f}</ProjectGuid>
<RootNamespace>cheat-base</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>cheat-base</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)bin\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)bin\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)bin\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)bin\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)bin\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)bin\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_AMD64_;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS;_CRT_SECURE_NO_WARNINGS;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir)framework\;$(ProjectDir)src\;$(ProjectDir)vendor\detours\;$(ProjectDir)vendor\fmt\include\;$(ProjectDir)vendor\imgui\;$(ProjectDir)vendor\magic_enum\include\;$(ProjectDir)vendor\simpleIni\;$(ProjectDir)vendor\json\single_include\;$(ProjectDir)vendor\imgui-notify-v2\;$(ProjectDir)vendor\stb\</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
<Lib>
<AdditionalDependencies>detours-x64.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
<Lib>
<AdditionalLibraryDirectories>$(ProjectDir)/vendor/detours/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>false</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_AMD64_;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS;_CRT_SECURE_NO_WARNINGS;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir)framework\;$(ProjectDir)src\;$(ProjectDir)vendor\detours\;$(ProjectDir)vendor\fmt\include\;$(ProjectDir)vendor\imgui\;$(ProjectDir)vendor\magic_enum\include\;$(ProjectDir)vendor\simpleIni\;$(ProjectDir)vendor\json\single_include\;$(ProjectDir)vendor\imgui-notify-v2\;$(ProjectDir)vendor\stb\</AdditionalIncludeDirectories>
<Optimization>Disabled</Optimization>
<WholeProgramOptimization>false</WholeProgramOptimization>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
<Lib>
<AdditionalDependencies>detours-x64.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
<Lib>
<AdditionalLibraryDirectories>$(ProjectDir)/vendor/detours/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>false</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_AMD64_;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS;_CRT_SECURE_NO_WARNINGS;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir)framework\;$(ProjectDir)src\;$(ProjectDir)vendor\detours\;$(ProjectDir)vendor\fmt\include\;$(ProjectDir)vendor\imgui\;$(ProjectDir)vendor\magic_enum\include\;$(ProjectDir)vendor\simpleIni\;$(ProjectDir)vendor\json\single_include\;$(ProjectDir)vendor\imgui-notify-v2\;$(ProjectDir)vendor\stb\</AdditionalIncludeDirectories>
<Optimization>Disabled</Optimization>
<WholeProgramOptimization>false</WholeProgramOptimization>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
<Lib>
<AdditionalDependencies>detours-x64.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
<Lib>
<AdditionalLibraryDirectories>$(ProjectDir)/vendor/detours/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="src\cheat-base\config\converters.h" />
<ClInclude Include="src\cheat-base\config\Field.h" />
<ClInclude Include="src\cheat-base\config\fields.h" />
<ClInclude Include="src\cheat-base\config\FieldSerialize.h" />
<ClInclude Include="src\cheat-base\config\fields\Enum.h" />
<ClInclude Include="src\cheat-base\config\fields\Toggle.h" />
<ClInclude Include="src\cheat-base\config\internal\FieldBase.h" />
<ClInclude Include="src\cheat-base\config\internal\FieldEntry.h" />
<ClInclude Include="src\cheat-base\config\internal\FieldSerialize.h" />
<ClInclude Include="src\cheat-base\config\Serializable.h" />
<ClInclude Include="src\cheat-base\config\FieldBase.h" />
<ClInclude Include="framework\framework.h" />
<ClInclude Include="framework\pch.h" />
<ClInclude Include="src\cheat-base\config\config.h" />
<ClInclude Include="src\cheat-base\globals.h" />
<ClInclude Include="src\cheat-base\events\event.hpp" />
<ClInclude Include="src\cheat-base\events\handlers\abstracteventhandler.hpp" />
<ClInclude Include="src\cheat-base\events\handlers\eventhandlerptr.h" />
<ClInclude Include="src\cheat-base\events\handlers\functoreventhandler.hpp" />
<ClInclude Include="src\cheat-base\events\handlers\handlercast.hpp" />
<ClInclude Include="src\cheat-base\events\handlers\helpers\innerholder.hpp" />
<ClInclude Include="src\cheat-base\events\handlers\helpers\objectsaver.hpp" />
<ClInclude Include="src\cheat-base\events\handlers\methodeventhandler.hpp" />
<ClInclude Include="src\cheat-base\events\helpers\is_equatable.hpp" />
<ClInclude Include="src\cheat-base\events\joins\abstracteventjoin.h" />
<ClInclude Include="src\cheat-base\events\joins\eventjoinwrapper.h" />
<ClInclude Include="src\cheat-base\events\joins\eventjoinwrapper.hpp" />
<ClInclude Include="src\cheat-base\events\joins\handlereventjoin.h" />
<ClInclude Include="src\cheat-base\events\joins\handlereventjoin.hpp" />
<ClInclude Include="src\cheat-base\ResourceLoader.h" />
<ClInclude Include="src\cheat-base\Hotkey.h" />
<ClInclude Include="src\cheat-base\render\ImageLoader.h" />
<ClInclude Include="src\cheat-base\PatternScanner.h" />
<ClInclude Include="src\cheat-base\cheat\CheatManagerBase.h" />
<ClInclude Include="src\cheat-base\cheat\Feature.h" />
<ClInclude Include="src\cheat-base\cheat\misc\Settings.h" />
<ClInclude Include="src\cheat-base\render\backend\dx11-hook.h" />
<ClInclude Include="src\cheat-base\render\gui-util.h" />
<ClInclude Include="src\cheat-base\render\renderer.h" />
<ClInclude Include="src\cheat-base\HookManager.h" />
<ClInclude Include="src\cheat-base\Patch.h" />
<ClInclude Include="src\cheat-base\thread-safe.h" />
<ClInclude Include="src\cheat-base\util.h" />
<ClInclude Include="src\cheat-base\Logger.h" />
<ClInclude Include="src\cheat-base\PipeTransfer.h" />
<ClInclude Include="vendor\detours\detours.h" />
<ClInclude Include="vendor\detours\detver.h" />
<ClInclude Include="vendor\fmt\include\fmt\args.h" />
<ClInclude Include="vendor\fmt\include\fmt\chrono.h" />
<ClInclude Include="vendor\fmt\include\fmt\color.h" />
<ClInclude Include="vendor\fmt\include\fmt\compile.h" />
<ClInclude Include="vendor\fmt\include\fmt\core.h" />
<ClInclude Include="vendor\fmt\include\fmt\format-inl.h" />
<ClInclude Include="vendor\fmt\include\fmt\format.h" />
<ClInclude Include="vendor\fmt\include\fmt\locale.h" />
<ClInclude Include="vendor\fmt\include\fmt\os.h" />
<ClInclude Include="vendor\fmt\include\fmt\ostream.h" />
<ClInclude Include="vendor\fmt\include\fmt\printf.h" />
<ClInclude Include="vendor\fmt\include\fmt\ranges.h" />
<ClInclude Include="vendor\fmt\include\fmt\xchar.h" />
<ClInclude Include="vendor\imgui-notify-v2\fa_solid_900.h" />
<ClInclude Include="vendor\imgui-notify-v2\font_awesome_5.h" />
<ClInclude Include="vendor\imgui-notify-v2\imgui_notify.h" />
<ClInclude Include="vendor\magic_enum\include\magic_enum.hpp" />
<ClInclude Include="vendor\simpleIni\SimpleIni.h" />
<ClInclude Include="vendor\stb\stb_image.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\cheat-base\config\config.cpp" />
<ClCompile Include="src\cheat-base\globals.cpp" />
<ClCompile Include="src\cheat-base\events\joins\abstracteventjoin.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="src\cheat-base\events\joins\eventjoinwrapper.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="src\cheat-base\ResourceLoader.cpp" />
<ClCompile Include="src\cheat-base\Hotkey.cpp" />
<ClCompile Include="src\cheat-base\render\ImageLoader.cpp" />
<ClCompile Include="src\cheat-base\PatternScanner.cpp" />
<ClCompile Include="src\cheat-base\cheat\CheatManagerBase.cpp" />
<ClCompile Include="src\cheat-base\cheat\misc\Settings.cpp" />
<ClCompile Include="src\cheat-base\render\backend\dx11-hook.cpp" />
<ClCompile Include="src\cheat-base\render\gui-util.cpp" />
<ClCompile Include="src\cheat-base\render\renderer.cpp" />
<ClCompile Include="src\cheat-base\Patch.cpp" />
<ClCompile Include="src\cheat-base\Logger.cpp" />
<ClCompile Include="src\cheat-base\util.cpp" />
<ClCompile Include="framework\pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="src\cheat-base\PipeTransfer.cpp" />
<ClCompile Include="vendor\fmt\src\format.cc">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="vendor\fmt\src\os.cc">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,279 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="framework\framework.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="framework\pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\util.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\PipeTransfer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\Logger.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\fmt\include\fmt\args.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\fmt\include\fmt\chrono.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\fmt\include\fmt\color.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\fmt\include\fmt\compile.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\fmt\include\fmt\core.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\fmt\include\fmt\format.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\fmt\include\fmt\format-inl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\fmt\include\fmt\locale.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\fmt\include\fmt\os.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\fmt\include\fmt\ostream.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\fmt\include\fmt\printf.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\fmt\include\fmt\ranges.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\fmt\include\fmt\xchar.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\magic_enum\include\magic_enum.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\simpleIni\SimpleIni.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\detours\detours.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\detours\detver.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\HookManager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\Patch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\thread-safe.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\render\gui-util.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\render\renderer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\render\backend\dx11-hook.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\cheat\CheatManagerBase.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\cheat\Feature.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\cheat\misc\Settings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\PatternScanner.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\Hotkey.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\stb\stb_image.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\render\ImageLoader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\ResourceLoader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\config\config.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\config\FieldBase.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\config\Serializable.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\config\fields.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\config\FieldSerialize.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\config\internal\FieldEntry.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\config\internal\FieldSerialize.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\config\Field.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\config\converters.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\config\fields\Enum.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\config\internal\FieldBase.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\imgui-notify-v2\fa_solid_900.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\imgui-notify-v2\font_awesome_5.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\imgui-notify-v2\imgui_notify.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\config\fields\Toggle.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\events\handlers\helpers\innerholder.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\events\handlers\helpers\objectsaver.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\events\handlers\abstracteventhandler.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\events\handlers\eventhandlerptr.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\events\handlers\functoreventhandler.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\events\handlers\handlercast.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\events\handlers\methodeventhandler.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\events\helpers\is_equatable.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\events\joins\abstracteventjoin.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\events\joins\eventjoinwrapper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\events\joins\eventjoinwrapper.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\events\joins\handlereventjoin.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\events\joins\handlereventjoin.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\events\event.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\globals.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\cheat-base\util.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="framework\pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\PipeTransfer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\Logger.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\Patch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\render\gui-util.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\render\renderer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\render\backend\dx11-hook.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\cheat\CheatManagerBase.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="vendor\fmt\src\os.cc">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="vendor\fmt\src\format.cc">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\cheat\misc\Settings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\PatternScanner.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\Hotkey.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\render\ImageLoader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\ResourceLoader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\config\config.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\events\joins\abstracteventjoin.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\events\joins\eventjoinwrapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\globals.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,3 @@
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers

View File

@ -0,0 +1,5 @@
// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.

View File

@ -0,0 +1,47 @@
// pch.h: This is a precompiled header file.
// Files listed below are compiled only once, improving build performance for future builds.
// This also affects IntelliSense performance, including code completion and many code browsing features.
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
// Do not add files here that you will be updating frequently as this negates the performance advantage.
#ifndef PCH_H
#define PCH_H
// add headers that you want to pre-compile here
#include "framework.h"
#include <magic_enum.hpp>
#include <nlohmann/json.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <map>
#include <unordered_set>
#include <queue>
#include <sstream>
#include <mutex>
#include <optional>
#include <Windows.h>
#include <imgui.h>
#include <imconfig.h>
#include <backends/imgui_impl_dx11.h>
#include <imgui_notify.h>
#include <SimpleIni.h>
#include <fmt/format.h>
#include <detours.h>
#include <cheat-base/Logger.h>
#include <cheat-base/events/event.hpp>
#include <cheat-base/events/handlers/methodeventhandler.hpp>
#include <cheat-base/events/handlers/functoreventhandler.hpp>
#include <cheat-base/events/joins/handlereventjoin.hpp>
#include <cheat-base/events/joins/eventjoinwrapper.hpp>
#include <cheat-base/globals.h>
#endif //PCH_H

View File

@ -0,0 +1,78 @@
#pragma once
#include <map>
#include <detours.h>
#define CALL_ORIGIN(function, ...) \
HookManager::call(function, __func__, __VA_ARGS__)
class HookManager
{
public:
template <typename Fn>
static void install(Fn func, Fn handler)
{
enable(func, handler);
holderMap[reinterpret_cast<void*>(handler)] = reinterpret_cast<void*>(func);
}
template <typename Fn>
[[nodiscard]] static Fn getOrigin(Fn handler, const char* callerName = nullptr) noexcept
{
if (holderMap.count(reinterpret_cast<void*>(handler)) == 0) {
LOG_WARNING("Origin not found for handler: %s. Maybe racing bug.", callerName == nullptr ? "<Unknown>" : callerName);
return nullptr;
}
return reinterpret_cast<Fn>(holderMap[reinterpret_cast<void*>(handler)]);
}
template <typename Fn>
[[nodiscard]] static void detach(Fn handler) noexcept
{
disable(handler);
holderMap.erase(reinterpret_cast<void*>(handler));
}
template <typename RType, typename... Params>
[[nodiscard]] static RType call(RType(*handler)(Params...), const char* callerName = nullptr, Params... params)
{
auto origin = getOrigin(handler, callerName);
if (origin != nullptr)
return origin(params...);
return RType();
}
static void detachAll() noexcept
{
for (const auto &[key, value] : holderMap)
{
disable(key);
}
holderMap.clear();
}
private:
inline static std::map<void*, void*> holderMap{};
template <typename Fn>
static void disable(Fn handler)
{
Fn origin = getOrigin(handler);
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)origin, handler);
DetourTransactionCommit();
}
template <typename Fn>
static void enable(Fn& func, Fn handler)
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)func, handler);
DetourTransactionCommit();
}
};

View File

@ -0,0 +1,395 @@
#include <pch.h>
#include "Hotkey.h"
#define IM_VK_KEYPAD_ENTER (VK_RETURN + 256)
static ImGuiKey LegacyToInput(short key)
{
switch (key)
{
case VK_TAB: return ImGuiKey_Tab;
case VK_LEFT: return ImGuiKey_LeftArrow;
case VK_RIGHT: return ImGuiKey_RightArrow;
case VK_UP: return ImGuiKey_UpArrow;
case VK_DOWN: return ImGuiKey_DownArrow;
case VK_PRIOR: return ImGuiKey_PageUp;
case VK_NEXT: return ImGuiKey_PageDown;
case VK_HOME: return ImGuiKey_Home;
case VK_END: return ImGuiKey_End;
case VK_INSERT: return ImGuiKey_Insert;
case VK_DELETE: return ImGuiKey_Delete;
case VK_BACK: return ImGuiKey_Backspace;
case VK_SPACE: return ImGuiKey_Space;
case VK_RETURN: return ImGuiKey_Enter;
case VK_ESCAPE: return ImGuiKey_Escape;
case VK_OEM_7: return ImGuiKey_Apostrophe;
case VK_OEM_COMMA: return ImGuiKey_Comma;
case VK_OEM_MINUS: return ImGuiKey_Minus;
case VK_OEM_PERIOD: return ImGuiKey_Period;
case VK_OEM_2: return ImGuiKey_Slash;
case VK_OEM_1: return ImGuiKey_Semicolon;
case VK_OEM_PLUS: return ImGuiKey_Equal;
case VK_OEM_4: return ImGuiKey_LeftBracket;
case VK_OEM_5: return ImGuiKey_Backslash;
case VK_OEM_6: return ImGuiKey_RightBracket;
case VK_OEM_3: return ImGuiKey_GraveAccent;
case VK_CAPITAL: return ImGuiKey_CapsLock;
case VK_SCROLL: return ImGuiKey_ScrollLock;
case VK_NUMLOCK: return ImGuiKey_NumLock;
case VK_SNAPSHOT: return ImGuiKey_PrintScreen;
case VK_PAUSE: return ImGuiKey_Pause;
case VK_NUMPAD0: return ImGuiKey_Keypad0;
case VK_NUMPAD1: return ImGuiKey_Keypad1;
case VK_NUMPAD2: return ImGuiKey_Keypad2;
case VK_NUMPAD3: return ImGuiKey_Keypad3;
case VK_NUMPAD4: return ImGuiKey_Keypad4;
case VK_NUMPAD5: return ImGuiKey_Keypad5;
case VK_NUMPAD6: return ImGuiKey_Keypad6;
case VK_NUMPAD7: return ImGuiKey_Keypad7;
case VK_NUMPAD8: return ImGuiKey_Keypad8;
case VK_NUMPAD9: return ImGuiKey_Keypad9;
case VK_DECIMAL: return ImGuiKey_KeypadDecimal;
case VK_DIVIDE: return ImGuiKey_KeypadDivide;
case VK_MULTIPLY: return ImGuiKey_KeypadMultiply;
case VK_SUBTRACT: return ImGuiKey_KeypadSubtract;
case VK_ADD: return ImGuiKey_KeypadAdd;
case IM_VK_KEYPAD_ENTER: return ImGuiKey_KeypadEnter;
case VK_LSHIFT: return ImGuiKey_LeftShift;
case VK_LCONTROL: return ImGuiKey_LeftCtrl;
case VK_LMENU: return ImGuiKey_LeftAlt;
case VK_LWIN: return ImGuiKey_LeftSuper;
case VK_RSHIFT: return ImGuiKey_RightShift;
case VK_RCONTROL: return ImGuiKey_RightCtrl;
case VK_RMENU: return ImGuiKey_RightAlt;
case VK_RWIN: return ImGuiKey_RightSuper;
case VK_APPS: return ImGuiKey_Menu;
case '0': return ImGuiKey_0;
case '1': return ImGuiKey_1;
case '2': return ImGuiKey_2;
case '3': return ImGuiKey_3;
case '4': return ImGuiKey_4;
case '5': return ImGuiKey_5;
case '6': return ImGuiKey_6;
case '7': return ImGuiKey_7;
case '8': return ImGuiKey_8;
case '9': return ImGuiKey_9;
case 'A': return ImGuiKey_A;
case 'B': return ImGuiKey_B;
case 'C': return ImGuiKey_C;
case 'D': return ImGuiKey_D;
case 'E': return ImGuiKey_E;
case 'F': return ImGuiKey_F;
case 'G': return ImGuiKey_G;
case 'H': return ImGuiKey_H;
case 'I': return ImGuiKey_I;
case 'J': return ImGuiKey_J;
case 'K': return ImGuiKey_K;
case 'L': return ImGuiKey_L;
case 'M': return ImGuiKey_M;
case 'N': return ImGuiKey_N;
case 'O': return ImGuiKey_O;
case 'P': return ImGuiKey_P;
case 'Q': return ImGuiKey_Q;
case 'R': return ImGuiKey_R;
case 'S': return ImGuiKey_S;
case 'T': return ImGuiKey_T;
case 'U': return ImGuiKey_U;
case 'V': return ImGuiKey_V;
case 'W': return ImGuiKey_W;
case 'X': return ImGuiKey_X;
case 'Y': return ImGuiKey_Y;
case 'Z': return ImGuiKey_Z;
case VK_F1: return ImGuiKey_F1;
case VK_F2: return ImGuiKey_F2;
case VK_F3: return ImGuiKey_F3;
case VK_F4: return ImGuiKey_F4;
case VK_F5: return ImGuiKey_F5;
case VK_F6: return ImGuiKey_F6;
case VK_F7: return ImGuiKey_F7;
case VK_F8: return ImGuiKey_F8;
case VK_F9: return ImGuiKey_F9;
case VK_F10: return ImGuiKey_F10;
case VK_F11: return ImGuiKey_F11;
case VK_F12: return ImGuiKey_F12;
case VK_LBUTTON: return ImGuiMouseButton_Left;
case VK_RBUTTON: return ImGuiMouseButton_Right;
case VK_MBUTTON: return ImGuiMouseButton_Middle;
case VK_XBUTTON1: return 3;
case VK_XBUTTON2: return 4;
default: return ImGuiKey_None;
}
}
static short InputToLegacy(ImGuiKey inputkey)
{
auto& io = ImGui::GetIO();
if (inputkey > 4)
return io.KeyMap[inputkey];
switch (inputkey)
{
case ImGuiMouseButton_Left:
return VK_LBUTTON;
case ImGuiMouseButton_Right:
return VK_RBUTTON;
case ImGuiMouseButton_Middle:
return VK_MBUTTON;
case 3:
return VK_XBUTTON1;
case 4:
return VK_XBUTTON2;
}
LOG_CRIT("Failed to find legacy input");
return -1;
}
static bool IsKeyDown(ImGuiKey key)
{
if (key > 6)
return ImGui::IsKeyDown(key);
switch (key)
{
case 1:
case 2:
return ImGui::IsMouseDown(key - 1);
case 4:
case 5:
case 6:
return ImGui::IsMouseDown(key - 2);
}
return false;
}
static bool IsKeyReleased(ImGuiKey key)
{
if (key > 6)
return ImGui::IsKeyReleased(key);
switch (key)
{
case 1:
case 2:
return ImGui::IsMouseReleased(key - 1);
case 4:
case 5:
case 6:
return ImGui::IsMouseReleased(key - 2);
}
return false;
}
void FixModKey(short& legacyKey)
{
// Can cause incorrect input when both keys pressed, need to fix!
switch (legacyKey)
{
case VK_CONTROL:
{
if (IsKeyDown(ImGuiKey_LeftCtrl))
legacyKey = VK_LCONTROL;
else if (IsKeyDown(ImGuiKey_RightCtrl))
legacyKey = VK_RCONTROL;
return;
}
case VK_SHIFT:
{
if (IsKeyDown(ImGuiKey_LeftShift))
legacyKey = VK_LSHIFT;
else if (IsKeyDown(ImGuiKey_RightShift))
legacyKey = VK_RSHIFT;
return;
}
}
}
Hotkey::Hotkey() : PressedEvent(m_PressedEvent), m_PressedEvent()
{
events::KeyUpEvent += MY_METHOD_HANDLER(Hotkey::OnKeyUp);
}
Hotkey::Hotkey(std::vector<short> legacyKeys) : Hotkey()
{
for (short legacyKey : legacyKeys)
{
this->m_Keys.insert(legacyKey);
}
}
Hotkey::Hotkey(short key) : Hotkey()
{
this->m_Keys.insert(key);
}
Hotkey::Hotkey(const Hotkey& other) : Hotkey()
{
m_Keys = {other.m_Keys};
}
Hotkey::~Hotkey()
{
events::KeyUpEvent -= MY_METHOD_HANDLER(Hotkey::OnKeyUp);
}
Hotkey& Hotkey::operator=(Hotkey&& hotkey) noexcept
{
m_Keys = std::move(hotkey.m_Keys);
return *this;
}
Hotkey& Hotkey::operator=(Hotkey& hotkey) noexcept
{
m_Keys = hotkey.m_Keys;
return *this;
}
bool Hotkey::operator-(const Hotkey& c2)
{
for (short key : m_Keys)
{
if (c2.m_Keys.count(key) == 0)
return true;
}
return false;
}
bool Hotkey::operator!=(const Hotkey& c2) const
{
return !(*this == c2);
}
bool Hotkey::operator==(const Hotkey& c2) const
{
return m_Keys == c2.m_Keys;
}
std::string GetKeyName(short key)
{
if (key > 5)
return ImGui::GetKeyName(key);
switch (key)
{
case ImGuiMouseButton_Left:
return "LMB";
case ImGuiMouseButton_Right:
return "RMB";
case ImGuiMouseButton_Middle:
return "MMB";
case 3:
return "Mouse X1";
case 4:
return "Mouse X2";
}
return "Unknown";
}
Hotkey::operator std::string() const
{
if (IsEmpty())
return "None";
std::stringstream hotkeyNameStream;
for (auto it = m_Keys.begin(); it != m_Keys.end(); it++)
{
if (it != m_Keys.begin())
hotkeyNameStream << " + ";
hotkeyNameStream << GetKeyName(LegacyToInput(*it));
}
return hotkeyNameStream.str();
}
bool Hotkey::IsPressed() const
{
for (short key : m_Keys)
{
if (!IsKeyDown(key))
return false;
}
return true;
}
bool Hotkey::IsPressed(short legacyKey) const
{
FixModKey(legacyKey);
if (m_Keys.count(legacyKey) == 0)
return false;
std::unordered_set<short> keysClone = m_Keys;
keysClone.erase(legacyKey);
for (short key : keysClone)
{
bool result = IsKeyDown(key);
if (!result)
return false;
}
return true;
}
bool Hotkey::IsReleased() const
{
bool released = false;
for (short key : m_Keys)
{
if (IsKeyReleased(key))
{
released = true;
continue;
}
if (!IsKeyDown(key))
return false;
}
return released;
}
bool Hotkey::IsEmpty() const
{
return m_Keys.size() == 0;
}
std::vector<short> Hotkey::GetKeys() const
{
return std::vector<short>(m_Keys.begin(), m_Keys.end());
}
Hotkey Hotkey::GetPressedHotkey()
{
Hotkey hotkey{};
auto& io = ImGui::GetIO();
for (ImGuiKey i = ImGuiKey_NamedKey_BEGIN; i < ImGuiKey_NamedKey_END - 4; i++)
{
bool isKeyDown = io.KeysDown[i];
if (isKeyDown)
hotkey.m_Keys.insert(InputToLegacy(i));
}
for (ImGuiKey i = 0; i < ImGuiMouseButton_COUNT; i++)
{
bool isMouseButtonDown = io.MouseDown[i];
if (isMouseButtonDown)
hotkey.m_Keys.insert(InputToLegacy(i));
}
return hotkey;
}
void Hotkey::OnKeyUp(short key, bool& canceled)
{
if (IsPressed(key))
m_PressedEvent();
}

View File

@ -0,0 +1,43 @@
#pragma once
#include <string>
#include <unordered_set>
#include <cheat-base/events/event.hpp>
class Hotkey
{
public:
Hotkey();
Hotkey(const Hotkey& other);
Hotkey(short key);
Hotkey(std::vector<short> keys);
~Hotkey();
Hotkey& operator=(Hotkey& hotkey) noexcept;
Hotkey& operator=(Hotkey&& hotkey) noexcept;
bool operator== (const Hotkey& c2) const;
bool operator!= (const Hotkey& c2) const;
bool operator-(const Hotkey& c2);
bool IsPressed() const;
bool IsPressed(short keyDown) const;
bool IsReleased() const;
bool IsEmpty() const;
std::vector<short> GetKeys() const;
operator std::string() const;
static Hotkey GetPressedHotkey();
IEvent<>& PressedEvent;
private:
TEvent<> m_PressedEvent;
std::unordered_set<short> m_Keys;
void OnKeyUp(short key, bool& canceled);
};

View File

@ -0,0 +1,143 @@
#include <pch.h>
#include "Logger.h"
#include <Windows.h>
#include <iostream>
#include <chrono>
#include <cstdarg>
#include <fstream>
#include <filesystem>
#include <cheat-base/util.h>
Logger::Level Logger::s_FileLogLevel = Logger::Level::None;
Logger::Level Logger::s_ConsoleLogLevel = Logger::Level::None;
std::string Logger::directory = "";
std::string Logger::logfilepath = "";
std::mutex Logger::_mutex{};
void Logger::SetLevel(Level level, LoggerType type)
{
switch (type)
{
case Logger::LoggerType::Any:
s_FileLogLevel = level;
s_ConsoleLogLevel = level;
break;
case Logger::LoggerType::ConsoleLogger:
s_ConsoleLogLevel = level;
break;
case Logger::LoggerType::FileLogger:
s_FileLogLevel = level;
break;
default:
break;
}
}
Logger::Level Logger::GetLevel(Logger::LoggerType type)
{
switch (type)
{
case Logger::LoggerType::Any:
return s_FileLogLevel < s_ConsoleLogLevel ? s_FileLogLevel : s_ConsoleLogLevel;
case Logger::LoggerType::ConsoleLogger:
return s_ConsoleLogLevel;
case Logger::LoggerType::FileLogger:
return s_FileLogLevel;
default:
return Logger::Level::None;
}
}
void LogToFile(std::string& filepath, std::string& msg)
{
std::ofstream myfile;
myfile.open(filepath, std::ios::out | std::ios::app | std::ios::binary);
myfile << msg << std::endl;
myfile.close();
}
struct Prefix
{
char color;
const char* text;
};
Prefix GetLevelPrefix(Logger::Level level)
{
switch (level)
{
case Logger::Level::Critical:
return { 0x04, "Critical" };
case Logger::Level::Error:
return { 0x0C, "Error" };
case Logger::Level::Warning:
return { 0x06, "Warning" };
case Logger::Level::Info:
return { 0x02, "Info" };
case Logger::Level::Debug:
return { 0x0B, "Debug" };
case Logger::Level::Trace:
return { 0x08, "Trace" };
default:
return { 0x00, "" };
}
}
void Logger::Log(Logger::Level logLevel, const char* filepath, int line, const char* fmt, ...)
{
if (Logger::s_ConsoleLogLevel == Logger::Level::None && Logger::s_FileLogLevel == Logger::Level::None)
return;
auto filename = std::filesystem::path(filepath).filename().string();
auto prefix = GetLevelPrefix(logLevel);
char buffer[1024];
va_list args;
va_start(args, fmt);
vsprintf_s(buffer, fmt, args);
va_end(args);
if (Logger::s_ConsoleLogLevel != Logger::Level::None && Logger::s_ConsoleLogLevel >= logLevel)
{
const std::lock_guard<std::mutex> lock(_mutex);
auto logLineConsole = util::string_format("[%s:%d] %s", filename.c_str(), line, buffer);
std::cout << "[";
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, prefix.color);
std::cout << prefix.text;
SetConsoleTextAttribute(hConsole, 15);
std::cout << "] " << logLineConsole << std::endl;
}
if (Logger::s_FileLogLevel != Logger::Level::None && Logger::s_FileLogLevel >= logLevel)
{
const std::lock_guard<std::mutex> lock(_mutex);
auto rawTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
struct tm gmtm;
gmtime_s(&gmtm, &rawTime);
auto logLineFile = util::string_format("[%02d:%02d:%02d] [%s] [%s:%d] %s", gmtm.tm_hour, gmtm.tm_min, gmtm.tm_sec,
prefix.text, filename.c_str(), line, buffer);
LogToFile(Logger::logfilepath, logLineFile);
}
}
void Logger::PrepareFileLogging(std::string directory)
{
auto rawTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
struct tm gmtm;
gmtime_s(&gmtm, &rawTime);
Logger::directory = directory;
if (!std::filesystem::is_directory(directory))
std::filesystem::create_directories(directory);
Logger::logfilepath = util::string_format("%s\\log_%04d-%02d-%02d_%02d-%02d.txt", directory.c_str(),
1900 + gmtm.tm_year, gmtm.tm_mon, gmtm.tm_mday, gmtm.tm_hour, gmtm.tm_min);
}

View File

@ -0,0 +1,48 @@
#pragma once
#include <string>
#include <mutex>
#define EXTLOG(level, fmt, ...) Logger::Log(level, __FILE__, __LINE__, fmt, __VA_ARGS__)
#define LOG_CRIT(fmt, ...) EXTLOG(Logger::Level::Critical, fmt, __VA_ARGS__)
#define LOG_ERROR(fmt, ...) EXTLOG(Logger::Level::Error, fmt, __VA_ARGS__)
#define LOG_WARNING(fmt, ...) EXTLOG(Logger::Level::Warning, fmt, __VA_ARGS__)
#define LOG_INFO(fmt, ...) EXTLOG(Logger::Level::Info, fmt, __VA_ARGS__)
#define LOG_DEBUG(fmt, ...) EXTLOG(Logger::Level::Debug, fmt, __VA_ARGS__)
#define LOG_TRACE(fmt, ...) EXTLOG(Logger::Level::Trace, fmt, __VA_ARGS__)
class Logger
{
public:
enum class Level
{
None,
Critical,
Error,
Warning,
Info,
Debug,
Trace
};
enum class LoggerType
{
Any,
ConsoleLogger,
FileLogger
};
static void SetLevel(Level level, LoggerType type = LoggerType::Any);
static Level GetLevel(LoggerType type);
static void Log(Level logLevel, const char* filename, int line, const char* fmt, ...);
static void PrepareFileLogging(std::string directory);
private:
static Level s_FileLogLevel;
static Level s_ConsoleLogLevel;
static std::mutex _mutex;
static std::string directory;
static std::string logfilepath;
};

View File

@ -0,0 +1,95 @@
#include <pch.h>
#include "Patch.h"
#include <cheat-base/util.h>
bool Patch::Install(uint64_t address, std::vector<uint8_t> value)
{
if (patches.count(address) > 0)
{
LOG_ERROR("Failed to install patch: patch already installed.");
return false;
}
auto oldValue = WriteMemory(address, value);
if (oldValue == nullptr)
return false;
patches[address] = oldValue;
return true;
}
bool Patch::Restore(uint64_t address)
{
if (patches.count(address) == 0)
{
LOG_ERROR("Failed to restore patch: not found patch to target address 0x%016X", address);
return false;
}
auto restoreValue = patches[address];
auto oldValue = WriteMemory(address, *restoreValue);
if (oldValue == nullptr)
return false;
patches.erase(address);
delete restoreValue;
delete oldValue;
return true;
}
std::vector<uint8_t>* Patch::WriteMemory(uint64_t address, std::vector<uint8_t> value)
{
MEMORY_BASIC_INFORMATION information{};
auto size = VirtualQuery(reinterpret_cast<void*>(address), &information, sizeof(information));
if (size < sizeof(information))
{
LOG_LAST_ERROR("Failed to get page information");
return nullptr;
}
if (information.State != MEM_COMMIT)
{
LOG_ERROR("Page at target address isn't MEM_COMMIT (0x%016X)", address);
return nullptr;
}
DWORD oldProtection = -1;
if ((information.AllocationProtect & PAGE_READWRITE) == 0 && (information.AllocationProtect & PAGE_EXECUTE_READWRITE) == 0)
{
if (VirtualProtect(reinterpret_cast<void*>(address), value.size(), PAGE_EXECUTE_READWRITE, &oldProtection) == FALSE)
{
LOG_LAST_ERROR("Failed to change page protection");
return nullptr;
}
}
auto oldValue = new std::vector<uint8_t>(value.size());
auto errorCode = memcpy_s(oldValue->data(), value.size(), reinterpret_cast<void*>(address), value.size());
if (errorCode != 0)
{
LOG_ERROR("Failed to get origin value from memory at 0x%016X. Error code: %d", address, errorCode);
delete oldValue;
return nullptr;
}
errorCode = memcpy_s(reinterpret_cast<void*>(address), value.size(), value.data(), value.size());
if (errorCode != 0)
{
LOG_ERROR("Failed to rewrite target memory at 0x%016X. Error code: %d", address, errorCode);
delete oldValue;
return nullptr;
}
if (oldProtection != -1)
{
DWORD temp = 0;
if (VirtualProtect(reinterpret_cast<void*>(address), value.size(), oldProtection, &temp) == FALSE)
{
LOG_LAST_ERROR("Failed to restore page protection");
}
}
return oldValue;
}

View File

@ -0,0 +1,31 @@
#pragma once
#include <vector>
#include <map>
#define OPatch(offset, value) Patch::Install(il2cppi_get_base_address() + offset, value)
#define OUnpatch(offset) Patch::Restore(il2cppi_get_base_address() + offset)
#define TogglePatch(field, targetField, offset, patchBytes) if (field == &targetField) { if (targetField.GetValue()) OPatch(offset, patchBytes); else OUnpatch(offset); return; }
class Patch
{
public:
// Installing patch to target address.
// In detail: replaces memory in target address with specified in 'value'
// saves old memory for future restore
// Return true if successfull.
static bool Install(uint64_t address, std::vector<uint8_t> value);
// Restoring old memory in this address, if it was patched.
// Return true if successfull.
static bool Restore(uint64_t address);
private:
inline static std::map<uint64_t, std::vector<uint8_t>*> patches{};
static std::vector<uint8_t>* WriteMemory(uint64_t address, std::vector<uint8_t> value);
};

View File

@ -0,0 +1,908 @@
#include "pch.h"
#include "PatternScanner.h"
#include <psapi.h>
#include <fstream>
#include <functional>
#include <cheat-base/util.h>
PatternScanner::PatternScanner() :
m_CacheChanged(false)
{
}
void PatternScanner::ParseSignatureFile(const std::string& signaturesContent)
{
nlohmann::json siganturesJson;
try
{
siganturesJson = nlohmann::json::parse(signaturesContent);
}
catch (nlohmann::json::parse_error* e)
{
LOG_ERROR("Failed to parse siganature json content. Error byte %d", e->byte);
return;
}
ParseSignatureJson(&siganturesJson);
}
bool PatternScanner::IsUpdated()
{
return m_CacheChanged;
}
void PatternScanner::ParseSignatureJson(void* signatureJson)
{
nlohmann::json& jsonContent = *reinterpret_cast<nlohmann::json*>(signatureJson);
for (auto& moduleEntry : jsonContent.items())
{
std::string moduleName = moduleEntry.key();
if (m_ModulePatterns.count(moduleName) == 0)
m_ModulePatterns[moduleName] = {};
auto& functionPatterns = m_ModulePatterns[moduleName];
for (auto& functionEntry : moduleEntry.value().items())
{
std::string functionName = functionEntry.key();
functionPatterns[functionName] = {};
auto& container = functionPatterns[functionName];
auto& patternInfo = functionEntry.value();
for (auto& xref : patternInfo["xref"])
container.xrefs.push_back({ xref["sig"], xref["offset"] });
for (auto& pattern : patternInfo["signatures"])
container.signatures.push_back(pattern);
}
}
}
uintptr_t PatternScanner::GetOffsetInt(const nlohmann::json& value)
{
std::uintptr_t offset = 0;
if (value.is_string())
{
std::string strValue = value;
offset = strtoul(strValue.c_str(), nullptr, 16);
}
else if (value.is_number_unsigned())
{
offset = value;
}
return offset;
}
std::string PatternScanner::GetOffsetStr(uintptr_t offset)
{
std::stringstream ss;
ss << std::hex << offset;
return ss.str();
}
void PatternScanner::Save(const std::filesystem::path& filename)
{
std::ofstream outputStream(filename, std::ios::out);
if (!outputStream.is_open())
{
LOG_ERROR("Failed to open file '%s' to save offsets.", filename.c_str());
return;
}
std::string output;
Save(output);
outputStream << output;
outputStream.close();
}
void PatternScanner::Save(std::string& outContent)
{
if (!m_CacheChanged)
outContent = m_LoadCache;
nlohmann::json modulesInfo{};
SaveJson(modulesInfo);
outContent = modulesInfo.dump();
}
void PatternScanner::SaveJson(nlohmann::json& outObject)
{
for (auto& [moduleName, functionsOffsets] : m_CacheOffsets)
{
SaveModuleHash(moduleName, outObject[moduleName]["hash"]);
auto& functionsObject = outObject[moduleName]["functions"];
for (auto& [functionName, offset] : functionsOffsets)
{
functionsObject[functionName] = GetOffsetStr(offset);
}
}
}
bool PatternScanner::Load(const std::filesystem::path& filename)
{
std::ifstream inputStream(filename, std::ios::in);
if (!inputStream.is_open())
{
LOG_ERROR("Failed to open file '%s' for load offsets.", filename.c_str());
return false;
}
std::stringstream buffer;
buffer << inputStream.rdbuf();
return Load(buffer.str());
}
bool PatternScanner::Load(const std::string& content)
{
nlohmann::json contentJson;
try
{
contentJson = nlohmann::json::parse(content);
}
catch (nlohmann::json::parse_error* e)
{
LOG_ERROR("Failed to parse siganature json content. Error byte %d", e->byte);
return false;
}
return LoadJson(contentJson);
m_LoadCache = content;
}
bool PatternScanner::LoadJson(const nlohmann::json& object)
{
bool result = true;
for (auto& moduleEntry : object.items())
{
std::string moduleName = moduleEntry.key();
auto& moduleJson = moduleEntry.value();
if (!IsValidModuleHash(moduleName, moduleJson["hash"]))
{
LOG_WARNING("Module '%s' hash don't match with saved one. Seems module was updated.", moduleName.c_str());
result = false;
continue;
}
if (m_CacheOffsets.count(moduleName) == 0)
m_CacheOffsets[moduleName] = {};
auto& functionsOffsets = m_CacheOffsets[moduleName];
for (auto& funcOffsetEntry : moduleJson["functions"].items())
{
functionsOffsets[funcOffsetEntry.key()] = GetOffsetInt(funcOffsetEntry.value());
}
}
return result;
}
PatternScanner::ModuleInfo& PatternScanner::GetModuleInfo(const std::string& modulePath)
{
static std::map<std::string, ModuleInfo> s_ModuleInfoCache;
std::string moduleName = std::filesystem::path(modulePath).filename().string();
if (s_ModuleInfoCache.count(moduleName) > 0)
return s_ModuleInfoCache[moduleName];
HMODULE hModule = GetModuleHandle(moduleName.c_str());
if (hModule == NULL)
{
LOG_LAST_ERROR("Failed to find module '%s'.", moduleName.c_str());
std::system("pause");
exit(0);
}
s_ModuleInfoCache[moduleName] = GetModuleInfo(hModule);
return s_ModuleInfoCache[moduleName];
}
PatternScanner::ModuleInfo& PatternScanner::GetModuleInfo(HMODULE hModule)
{
static std::map<HMODULE, ModuleInfo> s_ModuleInfoCache;
if (hModule == NULL)
{
LOG_CRIT("hModule is NULL.");
std::system("pause");
exit(0);
}
if (s_ModuleInfoCache.count(hModule) > 0)
return s_ModuleInfoCache[hModule];
MODULEINFO nativeInfo{};
BOOL result = GetModuleInformation(GetCurrentProcess(), hModule, &nativeInfo, sizeof(nativeInfo));
if (result == FALSE)
{
LOG_LAST_ERROR("Failed get info about module at 0x%p.", hModule);
std::system("pause");
exit(0);
}
s_ModuleInfoCache[hModule] = {};
auto& moduleInfo = s_ModuleInfoCache[hModule];
moduleInfo.handle = hModule;
moduleInfo.base = (uintptr_t)hModule;
moduleInfo.size = (uintptr_t)nativeInfo.SizeOfImage;
char buffer[MAX_PATH] = {};
result = GetModuleFileNameA(hModule, buffer, sizeof(buffer));
if (result == FALSE)
{
LOG_LAST_ERROR("Failed get filename module at 0x%p.", hModule);
std::system("pause");
exit(0);
}
moduleInfo.filePath = buffer;
LOG_DEBUG("Module %s bound 0x%p-0x%p.", moduleInfo.filePath.c_str(),
moduleInfo.base, moduleInfo.base + moduleInfo.size);
uintptr_t currentAddress = moduleInfo.base;
uintptr_t endAddress = moduleInfo.base + moduleInfo.size;
while (currentAddress <= endAddress)
{
MEMORY_BASIC_INFORMATION memoryInfo{};
auto byteCount = VirtualQuery((LPCVOID)currentAddress, &memoryInfo, sizeof(memoryInfo));
if (byteCount == 0)
{
LOG_LAST_ERROR("Failed get memory info for address 0x%p.", currentAddress);
break;
}
currentAddress = (uintptr_t)memoryInfo.BaseAddress + memoryInfo.RegionSize;
auto protection = memoryInfo.AllocationProtect;
if (protection != PAGE_EXECUTE && protection != PAGE_EXECUTE_READ &&
protection != PAGE_EXECUTE_READWRITE && protection != PAGE_EXECUTE_WRITECOPY)
continue;
moduleInfo.execRegions.push_back({ (uintptr_t)memoryInfo.BaseAddress, memoryInfo.RegionSize });
}
return moduleInfo;
}
void PatternScanner::AddOffset(const std::string& moduleName, const std::string& name, uintptr_t offset)
{
if (m_CacheOffsets.count(moduleName) == 0)
m_CacheOffsets[moduleName] = {};
m_CacheOffsets[moduleName][name] = offset;
m_CacheChanged = true;
}
size_t ComputeChecksum(const std::string& filename)
{
std::ifstream file(filename, std::ios::in | std::ios::binary);
if (!file.is_open())
{
LOG_ERROR("Failed to compute file json: %s", filename.c_str());
return 0;
}
size_t sum = 0;
size_t word = 0;
while (file.read(reinterpret_cast<char*>(&word), sizeof(word))) {
sum += word;
}
if (file.gcount()) {
word &= (~0U >> ((sizeof(size_t) - file.gcount()) * 8));
sum += word;
}
return sum;
}
int64_t PatternScanner::GetModuleTimestamp(const std::string& moduleName)
{
auto& moduleInfo = GetModuleInfo(moduleName);
auto write_time = std::filesystem::last_write_time(moduleInfo.filePath);
return write_time.time_since_epoch().count();
}
int64_t PatternScanner::GetModuleTimestamp(HMODULE hModule)
{
auto& moduleInfo = GetModuleInfo(hModule);
auto write_time = std::filesystem::last_write_time(moduleInfo.filePath);
return write_time.time_since_epoch().count();
}
bool PatternScanner::IsValidModuleHash(const std::string& moduleName, const nlohmann::json& hashObject)
{
auto& info = GetModuleInfo(moduleName);
return IsValidModuleHash(info.handle, hashObject);
}
bool PatternScanner::IsValidModuleHash(HMODULE hModule, const nlohmann::json& hashObject)
{
if (!hashObject.contains("timestamp") || !hashObject.contains("checksum"))
return false;
int64_t currTimestamp = GetModuleTimestamp(hModule);
int64_t timestamp = hashObject["timestamp"];
size_t checksum = hashObject["checksum"];
// To increase speed, we don't check checksum if timestamp matches
if (timestamp == currTimestamp)
{
m_ComputedHashes[hModule] = checksum;
return true;
}
size_t currChecksum = m_ComputedHashes.count(hModule) > 0 ? m_ComputedHashes[hModule] : ComputeChecksum(GetModuleInfo(hModule).filePath);
m_ComputedHashes[hModule] = currChecksum;
return checksum == currChecksum;
}
void PatternScanner::SaveModuleHash(const std::string& moduleName, nlohmann::json& outObject)
{
auto& info = GetModuleInfo(moduleName);
SaveModuleHash(info.handle, outObject);
}
void PatternScanner::SaveModuleHash(HMODULE hModule, nlohmann::json& outObject)
{
auto& moduleInfo = GetModuleInfo(hModule);
auto write_time = std::filesystem::last_write_time(moduleInfo.filePath);
int64_t timestamp = write_time.time_since_epoch().count();
size_t checksum = m_ComputedHashes.count(hModule) > 0 ? m_ComputedHashes[hModule] : ComputeChecksum(moduleInfo.filePath);
outObject["timestamp"] = timestamp;
outObject["checksum"] = checksum;
m_ComputedHashes[hModule] = checksum;
}
void PatternScanner::SearchAll()
{
for (auto& [moduleName, methodsSignatures] : m_ModulePatterns)
{
for (auto& [methodName, sigInfo] : methodsSignatures)
{
LOG_DEBUG("Searching %s::%s", moduleName.c_str(), methodName.c_str());
auto searchResult = Search(moduleName, methodName);
if (searchResult == 0)
LOG_WARNING("Not found");
else
LOG_DEBUG("Found function at %s + %p", moduleName.c_str(), searchResult);
}
}
}
uintptr_t PatternScanner::Search(const std::string& name)
{
for (auto& [moduleName, modulePatternsData] : m_ModulePatterns)
{
if (modulePatternsData.count(name) > 0)
return Search(moduleName, name, modulePatternsData[name]);
}
return 0;
}
uintptr_t PatternScanner::Search(const std::string& moduleName, const std::string& name)
{
uintptr_t moduleBase = GetModuleInfo(moduleName).base;
if (m_CacheOffsets.count(moduleName) > 0 && m_CacheOffsets[moduleName].count(name) > 0)
{
uintptr_t offset = m_CacheOffsets[moduleName][name];
return offset != 0 ? moduleBase + offset : 0;
}
if (m_ModulePatterns.count(moduleName) > 0 && m_ModulePatterns[moduleName].count(name) > 0)
return Search(moduleName, name, m_ModulePatterns[moduleName][name]);
AddOffset(moduleName, name, 0);
return 0;
}
uintptr_t PatternScanner::Search(const std::string& moduleName, const std::string& name, const PatternInfo& info)
{
AddressCounter counter;
uintptr_t moduleBase = GetModuleInfo(moduleName).base;
for (auto& pattern : info.signatures)
{
auto address = SearchFunction(moduleName, pattern);
if (address == 0)
continue;
counter.Add(address);
}
for (auto& xrefPattern : info.xrefs)
{
auto address = SearchXref(moduleName, xrefPattern);
if (address == 0)
continue;
counter.Add(address);
}
uintptr_t address = counter.GetMax();
AddOffset(moduleName, name, address == 0 ? 0 : address - moduleBase);
return address;
}
uintptr_t PatternScanner::SearchFunction(const std::string& moduleName, const std::string& pattern)
{
auto address = SearchInModule(moduleName, pattern);
if (address == 0)
return 0;
return FindFunctionEntry(address);
}
uintptr_t PatternScanner::SearchXref(const std::string& moduleName, const OffsetSignature& xrefPattern)
{
HMODULE hModule = GetModuleInfo(moduleName).handle;
return SearchXref(hModule, xrefPattern);
}
uintptr_t PatternScanner::SearchXref(HMODULE hModule, const OffsetSignature& xrefPattern)
{
auto address = SearchInModule(hModule, xrefPattern.pattern);
if (!address)
return 0;
uint8_t callOpcode = util::ReadMapped<uint8_t>((void*)address, xrefPattern.offset, true);
int opcodeOffset = 0;
switch (callOpcode)
{
case 0xE8:
case 0xE9:
opcodeOffset = 1;
break;
case 0x48:
case 0x4C:
opcodeOffset = 3;
break;
default:
LOG_WARNING("Trying find xref to not supported long call (opcode 0x%x)", callOpcode);
return 0;
}
int callOffset = util::ReadMapped<int>((void*)address, xrefPattern.offset + opcodeOffset, true);
uintptr_t dataAddress = address + xrefPattern.offset + 4 + opcodeOffset + callOffset;
//if (!IsFunctionEntry(functionAddress))
//{
// LOG_WARNING("Xref calc function address failed. There is no function at 0x%p.", functionAddress);
// return {};
//}
return dataAddress;
}
uintptr_t PatternScanner::SearchInModule(const std::string& moduleName, const std::string& pattern)
{
auto& moduleInfo = GetModuleInfo(moduleName);
auto tokens = util::StringSplit(" ", pattern);
std::vector<std::optional<uint8_t>> bytePattern;
bytePattern.reserve(tokens.size());
for (auto& token : tokens)
{
std::optional<uint8_t> value = token == "??" ? std::optional<uint8_t>() : std::stoi(token, 0, 16);
bytePattern.push_back(value);
}
uint8_t countFound = 0;
uint64_t address = 0;
for (auto& region : moduleInfo.execRegions)
{
auto regionSearchResult = SearchInRange(region.base, region.base + region.size, bytePattern);
if (regionSearchResult.status == SRStatus::NotFound)
continue;
if (regionSearchResult.status == SRStatus::NotUnique)
{
LOG_WARNING("Pattern ununique '%s'.", pattern.c_str());
return {};
}
countFound++;
if (countFound > 1)
{
LOG_WARNING("Pattern ununique '%s'.", pattern.c_str());
return {};
}
address = regionSearchResult.value;
}
if (countFound == 0)
LOG_WARNING("Pattern not found '%s'.", pattern.c_str());
return address;
}
uintptr_t PatternScanner::SearchInModule(HMODULE hModule, const std::string& pattern)
{
char buffer[MAX_PATH] = {};
auto count = GetModuleFileNameA(hModule, buffer, sizeof(buffer));
if (count == 0)
{
LOG_ERROR("Failed to get module name for handle 0x%p.", hModule);
return {};
}
return SearchInModule(buffer, pattern);
}
UINT32 get_first_bit_set(UINT32 x)
{
// Generates a single BSF instruction
unsigned long ret;
_BitScanForward(&ret, x);
return (UINT32)ret;
}
UINT32 clear_leftmost_set(UINT32 value)
{
// Generates a single BLSR instruction
return value & (value - 1);
}
int memcmp_mask(const BYTE* buffer1, const BYTE* buffer2, const BYTE* mask2, size_t count)
{
while (count--)
{
if (*mask2)
{
if (*buffer1 != *buffer2)
return -1;
}
buffer1++, buffer2++, mask2++;
};
return 0;
}
struct Signature
{
std::vector<uint8_t> bytes;
std::vector<uint8_t> mask;
bool hasWildcards = false;
Signature(const std::vector<std::optional<uint8_t>>& sig)
{
for (auto& item : sig)
{
if (!item)
{
bytes.push_back(0xCC);
mask.push_back(0x00);
hasWildcards = true;
continue;
}
bytes.push_back(*item);
mask.push_back(0xFF);
}
}
};
// Find signiture pattern in memory
PBYTE FindSignatureAVX2(PBYTE data, size_t size, const Signature& sig)
{
const auto* pat = sig.bytes.data();
size_t patLen = sig.bytes.size();
size_t patLen1 = (patLen - 1);
size_t patLen2 = (patLen - 2);
// Fill 'first' and 'last' with the first and last pattern byte respectively
const __m256i first = _mm256_set1_epi8(pat[0]);
const __m256i last = _mm256_set1_epi8(pat[patLen1]);
if (!sig.hasWildcards)
{
// A little faster without wildcards
// Scan 32 bytes at the time..
for (size_t i = 0; i + 32 + patLen1 < size; i += 32)
{
// Load in the next 32 bytes of input first and last
// Can use align 32 bit read for first since the input is page aligned
const __m256i block_first = _mm256_load_si256((const __m256i*) (data + i));
const __m256i block_last = _mm256_loadu_si256((const __m256i*) (data + i + patLen1));
// Compare first and last data to get 32byte masks
const __m256i eq_first = _mm256_cmpeq_epi8(first, block_first);
const __m256i eq_last = _mm256_cmpeq_epi8(last, block_last);
// AND the equality masks and into a 32 bit mask
UINT32 mask = _mm256_movemask_epi8(_mm256_and_si256(eq_first, eq_last));
// Do pattern compare between first and last position if we got our first and last at this data position
while (mask != 0)
{
UINT32 bitpos = get_first_bit_set(mask);
if (memcmp(data + i + bitpos + 1, pat + 1, patLen2) == 0)
return data + i + bitpos;
mask = clear_leftmost_set(mask);
};
}
}
else
{
// Pattern scan with wildcards mask
const BYTE* msk = sig.mask.data();
for (size_t i = 0; i + patLen1 + 32 < size; i += 32)
{
const __m256i block_first = _mm256_load_si256((const __m256i*) (data + i));
const __m256i block_last = _mm256_loadu_si256((const __m256i*) (data + i + patLen1));
const __m256i eq_first = _mm256_cmpeq_epi8(first, block_first);
const __m256i eq_last = _mm256_cmpeq_epi8(last, block_last);
UINT32 mask = _mm256_movemask_epi8(_mm256_and_si256(eq_first, eq_last));
// Do a byte pattern w/mask compare between first and last position if we got our first and last
while (mask != 0)
{
UINT32 bitpos = get_first_bit_set(mask);
if (memcmp_mask(data + i + bitpos + 1, pat + 1, msk + 1, patLen2) == 0)
return data + i + bitpos;
mask = clear_leftmost_set(mask);
};
}
}
return NULL;
}
PBYTE FindSignature(PBYTE input, size_t inputLen, const Signature& sig)
{
if (!sig.hasWildcards)
{
// If no wildcards, faster to use a memcmp() type
const BYTE* pat = sig.bytes.data();
const BYTE* end = (input + inputLen);
const BYTE first = *pat;
size_t sigLen = sig.bytes.size();
// Setup last in the pattern length byte quick for rejection test
size_t lastIdx = (sigLen - 1);
BYTE last = pat[lastIdx];
for (PBYTE ptr = input; ptr < end; ++ptr)
{
if ((ptr[0] == first) && (ptr[lastIdx] == last))
{
if (memcmp(ptr + 1, pat + 1, sigLen - 2) == 0)
return ptr;
}
}
}
else
{
const BYTE* pat = sig.bytes.data();
const BYTE* msk = sig.mask.data();
const BYTE* end = (input + inputLen);
const BYTE first = *pat;
size_t sigLen = sig.bytes.size();
size_t lastIdx = (sigLen - 1);
BYTE last = pat[lastIdx];
for (PBYTE ptr = input; ptr < end; ++ptr)
{
if ((ptr[0] == first) && (ptr[lastIdx] == last))
{
const BYTE* patPtr = pat + 1;
const BYTE* mskPtr = msk + 1;
const BYTE* memPtr = ptr + 1;
BOOL found = TRUE;
for (int i = 0; (i < sigLen - 2) && (memPtr < end); ++mskPtr, ++patPtr, ++memPtr, i++)
{
if (!*mskPtr)
continue;
if (*memPtr != *patPtr)
{
found = FALSE;
break;
}
}
if (found)
return ptr;
}
}
}
return 0;
}
PatternScanner::SearchResult PatternScanner::SearchSignatureAVX2(PBYTE input, size_t inputLen, const std::vector<std::optional<uint8_t>>& pattern)
{
Signature sig = Signature(pattern);
size_t sigSize = sig.bytes.size();
size_t len = inputLen;
size_t count = 0;
inputLen -= sigSize;
PBYTE match = FindSignatureAVX2(input, len, sig);
uintptr_t firstMatch = (uintptr_t)match;
while (match)
{
if (++count >= 2)
break;
++match;
len = (inputLen - (int)(match - input));
if (len < sigSize)
break;
match = FindSignatureAVX2(match, len, sig);
};
SearchResult result {};
result.value = (uintptr_t)firstMatch;
switch (count)
{
case 0: result.status = SRStatus::NotFound; break;
case 1: result.status = SRStatus::Unique; break;
default: result.status = SRStatus::NotUnique; break;
};
return result;
}
PatternScanner::SearchResult PatternScanner::SearchSignature(PBYTE input, size_t inputLen, const std::vector<std::optional<uint8_t>>& pattern)
{
Signature sig = Signature(pattern);
size_t sigSize = sig.bytes.size();
size_t len = inputLen;
size_t count = 0;
inputLen -= sigSize;
// Search for signature match..
PBYTE match = FindSignature(input, len, sig);
uintptr_t firstMatch = (uintptr_t)match;
while (match)
{
// Stop now if we've hit two matches
if (++count >= 2)
break;
++match;
len = (inputLen - (int)(match - input));
if (len < sigSize)
break;
// Next search
match = FindSignature(match, len, sig);
};
SearchResult result{};
result.value = (uintptr_t)firstMatch;
switch (count)
{
case 0: result.status = SRStatus::NotFound; break;
case 1: result.status = SRStatus::Unique; break;
default: result.status = SRStatus::NotUnique; break;
};
return result;
}
bool TestAVX2Support()
{
enum { EAX, EBX, ECX, EDX };
int regs[4];
// Highest Function Parameter
__cpuid(regs, 0);
if (regs[EAX] >= 7)
{
// Extended Features
__cpuid(regs, 7);
return (regs[EBX] & /*AVX2*/ (1 << 5)) != 0;
}
return false;
}
PatternScanner::SearchResult PatternScanner::SearchInRange(uintptr_t start, uintptr_t end, const std::vector<std::optional<uint8_t>>& pattern)
{
bool hasAVX2 = TestAVX2Support();
if (hasAVX2)
{
auto result = SearchSignatureAVX2((PBYTE)start, end - start, pattern);
return result;
}
static bool warnOnce = true;
if (warnOnce)
{
warnOnce = false;
LOG_WARNING("Using non-AVX2 reference search *\n");
}
return SearchSignature((PBYTE)start, end - start, pattern);
/*const uint8_t* rStart = (const uint8_t*)start;
const uint8_t* rEnd = (const uint8_t*)end;
auto comparer = [](uint8_t val1, std::optional<uint8_t> val2)
{
return (!val2 || val1 == *val2);
};
SearchResult sResult = { SRStatus::NotFound, 0 };
while (true)
{
const uint8_t* res = std::search(rStart, rEnd, pattern.begin(), pattern.end(), comparer);
if (res >= rEnd)
break;
if (sResult.status != SRStatus::NotFound)
return { SRStatus::NotUnique, 0 };
sResult = { SRStatus::Unique, (uint64_t)res };
rStart = res + pattern.size();
}
return sResult;*/
}
bool PatternScanner::IsFunctionEntry(uintptr_t functionAddress)
{
auto address = FindFunctionEntry(functionAddress);
if (functionAddress == 0)
return false;
return functionAddress == functionAddress;
}
uintptr_t PatternScanner::FindFunctionEntry(uintptr_t address)
{
// TODO: Implement function asm head find. Maybe with use Zydis.
return 0;
}
PatternScanner::AddressCounter::AddressCounter() {}
void PatternScanner::AddressCounter::Add(uintptr_t address)
{
if (m_Counts.count(address) == 0)
m_Counts[address] = 0;
m_Counts[address]++;
}
uintptr_t PatternScanner::AddressCounter::GetMax()
{
if (m_Counts.size() == 0)
return 0;
using pair_type = decltype(m_Counts)::value_type;
auto maxEntry = std::max_element(m_Counts.begin(), m_Counts.end(),
[](const pair_type& a, const pair_type& b)
{
return a.second < b.second;
});
return maxEntry->first;
}

View File

@ -0,0 +1,138 @@
#pragma once
#include <string>
#include <optional>
#include <map>
#include <filesystem>
#include <cheat-base/util.h>
class PatternScanner
{
public:
PatternScanner();
uintptr_t Search(const std::string& name);
virtual uintptr_t Search(const std::string& moduleName, const std::string& name);
virtual void SearchAll();
void Save(const std::filesystem::path& filename);
void Save(std::string& outContent);
bool Load(const std::filesystem::path& filename);
bool Load(const std::string& content);
virtual void SaveJson(nlohmann::json& outObject);
virtual bool LoadJson(const nlohmann::json& object);
void ParseSignatureFile(const std::string& signaturesContent);
bool IsUpdated();
bool IsValidModuleHash(HMODULE HMODULE, const nlohmann::json& hashObject);
bool IsValidModuleHash(const std::string& moduleName, const nlohmann::json& hashObject);
int64_t GetModuleTimestamp(const std::string& moduleName);
int64_t GetModuleTimestamp(HMODULE hModule);
protected:
class AddressCounter
{
public:
AddressCounter();
void Add(uintptr_t address);
uintptr_t GetMax();
private:
std::map<uintptr_t, size_t> m_Counts;
};
struct OffsetSignature
{
std::string pattern;
uint32_t offset;
};
struct PatternInfo
{
std::vector<std::string> signatures;
std::vector<OffsetSignature> xrefs;
};
std::map<std::string, std::map<std::string, PatternInfo>> m_ModulePatterns;
std::map<std::string, std::map<std::string, uintptr_t>> m_CacheOffsets;
bool m_CacheChanged;
std::string m_LoadCache;
std::map<HMODULE, int64_t> m_ComputedHashes;
struct RegionInfo
{
uintptr_t base;
size_t size;
};
struct ModuleInfo
{
HMODULE handle = 0;
uintptr_t base = 0;
size_t size = 0;
std::string filePath;
std::vector<RegionInfo> execRegions;
};
void AddOffset(const std::string& moduleName, const std::string& name, uintptr_t offset);
void SaveModuleHash(HMODULE hModule, nlohmann::json& outObject);
void SaveModuleHash(const std::string& moduleName, nlohmann::json& outObject);
ModuleInfo& GetModuleInfo(HMODULE hModule);
ModuleInfo& GetModuleInfo(const std::string& moduleName);
virtual void ParseSignatureJson(void* signatureJson);
virtual uintptr_t Search(const std::string& moduleName, const std::string& name, const PatternInfo& info);
uintptr_t SearchFunction(const std::string& moduleName, const std::string& pattern);
uintptr_t SearchXref(const std::string& moduleName, const OffsetSignature& xrefPattern);
uintptr_t SearchXref(HMODULE hModule, const OffsetSignature& xrefPattern);
uintptr_t SearchInModule(const std::string& moduleName, const std::string& pattern);
uintptr_t SearchInModule(HMODULE hModule, const std::string& pattern);
uintptr_t GetOffsetInt(const nlohmann::json& value);
std::string GetOffsetStr(uintptr_t offset);
template<typename R>
std::optional<R> SearchValue(HMODULE hModule, const std::string& pattern, uint32_t codeOffset)
{
auto address = SearchInModule(hModule, pattern);
if (address == 0)
return {};
int offset = util::ReadMapped<int>((void*)address, codeOffset, true);
return reinterpret_cast<R>(address + offset + codeOffset + sizeof(int));
}
enum class SRStatus
{
Unique,
NotUnique,
NotFound
};
struct SearchResult
{
SRStatus status;
uintptr_t value;
};
SearchResult SearchSignature(PBYTE input, size_t inputLen, const std::vector<std::optional<uint8_t>>& sig);
SearchResult SearchSignatureAVX2(PBYTE input, size_t inputLen, const std::vector<std::optional<uint8_t>>& sig);
SearchResult SearchInRange(uintptr_t start, uintptr_t end, const std::vector<std::optional<uint8_t>>& pattern);
virtual bool IsFunctionEntry(uintptr_t address);
virtual uintptr_t FindFunctionEntry(uintptr_t address);
};

View File

@ -0,0 +1,87 @@
#include <pch.h>
#include "PipeTransfer.h"
#include <iostream>
#include <sstream>
#include <cheat-base/util.h>
PipeTransfer::PipeTransfer(const std::string& name)
{
std::stringstream ss;
ss << "\\\\.\\pipe\\" << name;
this->m_Name = ss.str();
this->m_Pipe = 0;
}
PipeTransfer::~PipeTransfer()
{
if (m_Pipe)
CloseHandle(m_Pipe);
}
bool PipeTransfer::Create()
{
if (m_Pipe)
CloseHandle(m_Pipe);
m_Pipe = CreateNamedPipe(m_Name.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 256 * 1024, 16, INFINITE, NULL);
return IsPipeOpened();
}
bool PipeTransfer::IsPipeOpened()
{
return m_Pipe && m_Pipe != INVALID_HANDLE_VALUE;
}
bool PipeTransfer::Connect()
{
if (IsPipeOpened())
CloseHandle(m_Pipe);
m_Pipe = CreateFile(m_Name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
return IsPipeOpened();
}
bool PipeTransfer::WaitForConnection()
{
return ConnectNamedPipe(m_Pipe, nullptr);
}
void PipeTransfer::ReadBytes(void* buffer, size_t size)
{
if (size == 0 || !IsPipeOpened()) return;
DWORD readCount = 0;
auto result = ReadFile(m_Pipe, buffer, size, &readCount, nullptr);
if (!result || readCount < size)
{
LOG_LAST_ERROR("Failed read from pipe.");
CloseHandle(m_Pipe);
m_Pipe = 0;
}
}
void PipeTransfer::WriteBytes(void* buffer, size_t size)
{
if (size == 0 || !IsPipeOpened()) return;
DWORD writenCount = 0;
auto result = WriteFile(m_Pipe, buffer, size, &writenCount, nullptr);
if (!result || writenCount < size)
{
LOG_LAST_ERROR("Failed write to pipe.");
CloseHandle(m_Pipe);
m_Pipe = 0;
}
}
void PipeTransfer::ReadObject(PipeSerializedObject& object)
{
object.Read(this);
}
void PipeTransfer::WriteObject(PipeSerializedObject& object)
{
object.Write(this);
}

View File

@ -0,0 +1,82 @@
#pragma once
#include <string>
#include <vector>
typedef unsigned char byte;
class PipeTransfer;
class PipeSerializedObject
{
public:
virtual void Write(PipeTransfer* transfer) = 0;
virtual void Read(PipeTransfer* transfer) = 0;
};
class PipeTransfer
{
public:
PipeTransfer(const std::string& name);
~PipeTransfer();
bool Create();
bool Connect();
bool WaitForConnection();
bool IsPipeOpened();
void ReadBytes(void* buffer, size_t size);
void WriteBytes(void* buffer, size_t size);
void ReadObject(PipeSerializedObject& object);
void WriteObject(PipeSerializedObject& object);
template<class T>
void Read(T& value)
{
ReadBytes(&value, sizeof(T));
}
template<class T>
void Write(const T& val)
{
WriteBytes(const_cast<T*>(&val), sizeof(T));
}
template<>
void Read(std::vector<byte>& vector)
{
size_t size; Read(size);
vector.clear();
vector.resize(size);
ReadBytes(vector.data(), size);
}
template<>
void Write(const std::vector<byte>& value)
{
Write<size_t>(value.size());
WriteBytes(const_cast<byte*>(value.data()), value.size());
}
template<>
void Read(std::string& value)
{
size_t size; Read(size);
value.clear();
value.resize(size);
ReadBytes(value.data(), size);
}
template<>
void Write(const std::string& value)
{
Write<size_t>(value.length());
WriteBytes(const_cast<char*>(value.data()), value.length());
}
private:
std::string m_Name;
HANDLE m_Pipe;
};

View File

@ -0,0 +1,49 @@
#include "pch.h"
#include "ResourceLoader.h"
#include "util.h"
std::string ResourceLoader::Load(const char* name, const char* type)
{
LPBYTE pData = nullptr;
DWORD size = 0;
if (!LoadEx(name, type, pData, size))
{
LOG_LAST_ERROR("Failed to load resource %s", name);
return {};
}
return std::string(reinterpret_cast<char*>(pData), size);
}
std::string ResourceLoader::Load(int resID, const char* type)
{
return ResourceLoader::Load(MAKEINTRESOURCE(resID), type);
}
bool ResourceLoader::LoadEx(const char* name, const char* type, LPBYTE& pDest, DWORD& size)
{
if (s_Handle == nullptr)
return false;
HRSRC hResource = FindResource(s_Handle, name, type);
if (hResource) {
HGLOBAL hGlob = LoadResource(s_Handle, hResource);
if (hGlob) {
size = SizeofResource(s_Handle, hResource);
pDest = static_cast<LPBYTE>(LockResource(hGlob));
if (size > 0 && pDest)
return true;
}
}
return false;
}
bool ResourceLoader::LoadEx(int resId, const char* type, LPBYTE& pDest, DWORD& size)
{
return ResourceLoader::LoadEx(MAKEINTRESOURCE(resId), type, pDest, size);
}
void ResourceLoader::SetModuleHandle(HMODULE handle)
{
s_Handle = handle;
}

View File

@ -0,0 +1,16 @@
#pragma once
class ResourceLoader
{
public:
static std::string Load(const char* name, const char* type);
static std::string Load(int resID, const char* type);
static bool LoadEx(const char* name, const char* type, LPBYTE& pDest, DWORD& size);
static bool LoadEx(int resId, const char* type, LPBYTE& pDest, DWORD& size);
static void SetModuleHandle(HMODULE handle);
private:
inline static HMODULE s_Handle = nullptr;
};

View File

@ -0,0 +1,551 @@
#include <pch.h>
#include "CheatManagerBase.h"
#include <misc/cpp/imgui_stdlib.h>
#include <cheat-base/render/renderer.h>
#include <cheat-base/render/gui-util.h>
#include <cheat-base/cheat/misc/Settings.h>
namespace cheat
{
void CheatManagerBase::Init(LPBYTE pFontData, DWORD dFontDataSize)
{
renderer::Init(pFontData, dFontDataSize);
events::RenderEvent += MY_METHOD_HANDLER(CheatManagerBase::OnRender);
events::KeyUpEvent += MY_METHOD_HANDLER(CheatManagerBase::OnKeyUp);
events::WndProcEvent += MY_METHOD_HANDLER(CheatManagerBase::OnWndProc);
}
CheatManagerBase::CheatManagerBase():
NF(m_SelectedSection, "", "General", 0),
m_IsBlockingInput(true),
m_IsPrevCursorActive(false)
{
}
void CheatManagerBase::DrawExternal() const
{
for (auto& feature : m_Features)
{
ImGui::PushID(&feature);
feature->DrawExternal();
ImGui::PopID();
}
}
void CheatManagerBase::DrawMenu()
{
if (m_ModuleOrder.empty())
return;
static std::string* current = &m_ModuleOrder[m_SelectedSection];
ImGui::SetNextWindowSize(ImVec2(600, 300), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("CCGenshin (By Callow)"))
{
ImGui::End();
return;
}
ImGui::BeginGroup();
if (ImGui::Checkbox("Block key/mouse", &m_IsBlockingInput))
{
renderer::SetInputLock(this, m_IsBlockingInput);
}
if (ImGui::BeginListBox("##listbox 2", ImVec2(175, -FLT_MIN)))
{
size_t index = 0;
for (auto& moduleName : m_ModuleOrder)
{
const bool is_selected = (current == &moduleName);
if (ImGui::Selectable(moduleName.c_str(), is_selected))
{
current = &moduleName;
m_SelectedSection = index;
}
if (is_selected)
ImGui::SetItemDefaultFocus();
index++;
}
ImGui::EndListBox();
}
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
DrawProfileLine();
ImGuiWindowFlags window_flags = ImGuiWindowFlags_None;
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);
ImGui::BeginChild("ChildR", ImVec2(0, 0), true, window_flags);
auto& sections = m_FeatureMap[*current];
auto emptyName = std::string();
if (sections.count(emptyName) > 0)
DrawMenuSection(emptyName, sections[""]);
for (auto& [sectionName, features] : sections)
{
if (sectionName.empty())
continue;
DrawMenuSection(sectionName, features);
}
ImGui::EndChild();
ImGui::PopStyleVar();
ImGui::EndGroup();
ImGui::End();
}
void CheatManagerBase::DrawMenuSection(const std::string& sectionName, const std::vector<Feature*>& features) const
{
if (!sectionName.empty())
BeginGroupPanel(sectionName.c_str(), ImVec2(-1, 0));
for (auto& feature : features)
{
ImGui::PushID(&feature);
feature->DrawMain();
ImGui::PopID();
}
if (!sectionName.empty())
EndGroupPanel();
}
void CheatManagerBase::DrawProfileGlobalActivities()
{
if (ImGui::Button("Add new profile"))
{
std::unordered_set<std::string> profileNameSet = { config::GetProfiles().begin(), config::GetProfiles().end() };
size_t index = 0;
std::string name {};
do
{
index++;
std::string newName = fmt::format("Profile #{}", index);
if (profileNameSet.count(newName) == 0)
name = newName;
} while (name.empty());
config::CreateProfile(name, false);
}
}
void CheatManagerBase::DrawProfileEntryActivities(const std::string& profileName)
{
bool isPopupOpen = ImGui::IsRenamePopupOpened();
if (isPopupOpen)
ImGui::BeginDisabled();
if (ImGui::SmallButton("Rnm"))
ImGui::OpenRenamePopup(profileName);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("Rename");
if (isPopupOpen)
ImGui::EndDisabled();
std::string nameBuffer;
if (ImGui::DrawRenamePopup(nameBuffer))
{
config::RenameProfile(profileName, nameBuffer);
}
ImGui::SameLine();
if (ImGui::SmallButton("Del"))
config::RemoveProfile(profileName);
if (ImGui::IsItemHovered())
ImGui::SetTooltip("Delete");
}
void CheatManagerBase::DrawProfileEntry(const std::string& profileName)
{
ImGui::Text(profileName.c_str());
}
void CheatManagerBase::DrawProfileTableHeader()
{
ImGui::TableSetupColumn("Name");
}
int CheatManagerBase::GetProfileTableColumnCount()
{
return 1;
}
void CheatManagerBase::DrawProfileConfiguration()
{
static ImGuiTableFlags flags =
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable
| ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody
| ImGuiTableFlags_ScrollY;
if (ImGui::BeginTable("ProfileTable", GetProfileTableColumnCount() + 1, flags,
ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 10), 0.0f))
{
DrawProfileTableHeader();
ImGui::TableSetupColumn("Actions");
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableHeadersRow();
// Copy profiles names
auto profiles = config::GetProfiles();
for (auto& profile : profiles)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushID(profile.c_str());
DrawProfileEntry(profile);
ImGui::TableNextColumn();
DrawProfileEntryActivities(profile);
ImGui::PopID();
}
ImGui::EndTable();
}
DrawProfileGlobalActivities();
}
void CheatManagerBase::DrawProfileLine()
{
if (m_IsProfileConfigurationShowed)
ImGui::BeginDisabled();
bool buttonPressed = ImGui::Button("Configure...");
if (m_IsProfileConfigurationShowed)
ImGui::EndDisabled();
if (buttonPressed)
m_IsProfileConfigurationShowed = !m_IsProfileConfigurationShowed;
ImGui::SameLine();
auto& profiles = config::GetProfiles();
auto& currentProfile = config::CurrentProfileName();
constexpr float width = 200.0f;
ImGui::SetNextItemWidth(width);
if (ImGui::BeginCombo("Profile", currentProfile.c_str()))
{
for (auto& name : profiles)
{
bool is_selected = (currentProfile == name);
if (ImGui::Selectable(name.c_str(), is_selected))
config::ChangeProfile(name);
if (ImGui::IsItemHovered() && CalcWidth(name) > width)
ShowHelpText(name.c_str());
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
}
void CheatManagerBase::DrawStatus() const
{
// Drawing status window
ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse;
auto& settings = feature::Settings::GetInstance();
if (!settings.f_StatusMove)
flags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove;
ImGui::Begin("Cheat status", nullptr, flags);
static ImGuiTableFlags tabFlags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
if (ImGui::BeginTable("activesTable", 1, tabFlags))
{
ImGui::TableSetupColumn("Active features");
ImGui::TableHeadersRow();
int row = 0;
for (auto& feature : m_Features)
{
if (feature->NeedStatusDraw())
{
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
feature->DrawStatus();
ImU32 row_bg_color = ImGui::GetColorU32(
ImVec4(0.2f + row * 0.1f, 0.1f + row * 0.05f, 0.1f + row * 0.03f, 0.85f));
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, row_bg_color);
row++;
}
}
ImGui::EndTable();
}
ImGui::End();
}
void CheatManagerBase::DrawInfo()
{
auto& settings = feature::Settings::GetInstance();
// Drawing status window
ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse;
if (!settings.f_InfoMove)
flags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove;
auto checkLambda = [](const Feature* feat) { return feat->NeedInfoDraw(); };
bool showAny = std::any_of(m_Features.begin(), m_Features.end(), checkLambda);
if (!showAny && !settings.f_StatusMove)
return;
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.04f, 0.05f, 0.05f, 0.90f));
ImGui::Begin("Info window", nullptr, flags);
ImGui::PopStyleColor();
if (!showAny)
{
ImGui::Text("Nothing here");
ImGui::End();
return;
}
for (auto& moduleName : m_ModuleOrder)
{
auto& sections = m_FeatureMap[moduleName];
bool moduleShowAny = std::any_of(sections.begin(), sections.end(),
[](const auto& iter)
{
return std::any_of(iter.second.begin(), iter.second.end(),
[](const auto feat)
{
return feat->NeedInfoDraw();
});
}
);
if (!moduleShowAny)
continue;
BeginGroupPanel(moduleName.c_str(), ImVec2(-1, 0));
for (auto& [sectionName, features] : sections)
{
for (auto& feature : features)
{
if (!feature->NeedInfoDraw())
continue;
ImGui::PushID(&feature);
feature->DrawInfo();
ImGui::PopID();
}
}
EndGroupPanel();
}
ImGui::End();
}
void CheatManagerBase::DrawFps()
{
auto& settings = feature::Settings::GetInstance();
ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoFocusOnAppearing
| ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize;
if (!settings.f_FpsMove)
flags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove;
if (ImGui::Begin("FPS", nullptr, flags))
{
ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
ImGui::End();
}
}
void CheatManagerBase::DrawNotifications()
{
ImGui::RenderNotifications();
}
void CheatManagerBase::OnRender()
{
auto& settings = feature::Settings::GetInstance();
DrawExternal();
if (s_IsMenuShowed)
DrawMenu();
if (m_IsProfileConfigurationShowed)
{
ImGui::SetNextWindowSize({ 0, ImGui::GetTextLineHeightWithSpacing() * 11 }, ImGuiCond_FirstUseEver);
if (ImGui::Begin("Config profile configuration", &m_IsProfileConfigurationShowed))
DrawProfileConfiguration();
ImGui::End();
}
if (settings.f_StatusShow)
DrawStatus();
if (settings.f_InfoShow)
DrawInfo();
if (settings.f_FpsShow)
DrawFps();
if (settings.f_NotificationsShow)
DrawNotifications();
if (settings.f_MenuKey.value().IsReleased() && !ImGui::IsAnyItemActive())
ToggleMenuShow();
}
void CheatManagerBase::CheckToggles(short key) const
{
if (s_IsMenuShowed || renderer::IsInputLocked())
return;
auto& settings = feature::Settings::GetInstance();
if (!settings.f_HotkeysEnabled)
return;
for (auto& field : config::GetFields<config::Toggle<Hotkey>>())
{
auto& toggle = field.value();
if (toggle.value.IsPressed(key))
{
toggle.enabled = !toggle.enabled;
field.FireChanged();
std::string title = fmt::format("{}: {}", field.friendName(), (toggle ? "Enabled" : "Disabled"));
ImGuiToast toast(ImGuiToastType_None, settings.f_NotificationsDelay);
toast.set_title(title.c_str());
ImGui::InsertNotification(toast);
}
}
}
bool menuToggled = false;
void CheatManagerBase::ToggleMenuShow()
{
s_IsMenuShowed = !s_IsMenuShowed;
renderer::SetInputLock(this, s_IsMenuShowed && m_IsBlockingInput);
menuToggled = true;
}
void CheatManagerBase::OnKeyUp(short key, bool& cancelled)
{
auto& settings = feature::Settings::GetInstance();
if (!settings.f_MenuKey.value().IsPressed(key))
{
CheckToggles(key);
return;
}
}
void CheatManagerBase::OnWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool& canceled)
{
if (!menuToggled)
return;
menuToggled = false;
if (s_IsMenuShowed)
{
m_IsPrevCursorActive = CursorGetVisibility();
if (!m_IsPrevCursorActive)
CursorSetVisibility(true);
}
else if (!m_IsPrevCursorActive)
CursorSetVisibility(false);
}
bool CheatManagerBase::IsMenuShowed()
{
return s_IsMenuShowed;
}
void CheatManagerBase::PushFeature(Feature* feature)
{
m_Features.push_back(feature);
auto& info = feature->GetGUIInfo();
if (m_FeatureMap.count(info.moduleName) == 0)
{
m_FeatureMap[info.moduleName] = {};
m_ModuleOrder.push_back(info.moduleName);
}
auto& sectionMap = m_FeatureMap[info.moduleName];
std::string sectionName = info.isGroup ? info.name : std::string();
if (sectionMap.count(sectionName) == 0)
sectionMap[sectionName] = {};
auto& featureList = sectionMap[sectionName];
featureList.push_back(feature);
}
void CheatManagerBase::AddFeature(Feature* feature)
{
PushFeature(feature);
}
void CheatManagerBase::AddFeatures(std::vector<Feature*> features)
{
for (auto& feature : features)
{
PushFeature(feature);
}
}
void CheatManagerBase::SetModuleOrder(std::vector<std::string> moduleOrder)
{
std::unordered_set<std::string> moduleSet;
moduleSet.insert(m_ModuleOrder.begin(), m_ModuleOrder.end());
m_ModuleOrder.clear();
for (auto& moduleName : moduleOrder)
{
if (m_FeatureMap.count(moduleName) == 0)
continue;
m_ModuleOrder.push_back(moduleName);
moduleSet.erase(moduleName);
}
for (auto& moduleName : moduleSet)
{
m_ModuleOrder.push_back(moduleName);
}
}
}

View File

@ -0,0 +1,76 @@
#pragma once
#include <cheat-base/cheat/Feature.h>
#include <cheat-base/config/Config.h>
#include <cheat-base/events/event.hpp>
#include <vector>
#include <map>
#include <string>
#include <Windows.h>
namespace cheat
{
class CheatManagerBase
{
public:
static bool IsMenuShowed();
//static CheatManagerBase& GetInstance();
CheatManagerBase(CheatManagerBase const&) = delete;
void operator=(CheatManagerBase const&) = delete;
void AddFeature(Feature* feature);
void AddFeatures(std::vector<Feature*> features);
void SetModuleOrder(std::vector<std::string> moduleOrder);
void OnKeyUp(short key, bool& cancelled);
void OnWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool& cancelled);
void OnRender();
void Init(LPBYTE pFontData, DWORD dFontDataSize);
virtual void CursorSetVisibility(bool visibility) = 0;
virtual bool CursorGetVisibility() = 0;
protected:
config::Field<size_t> m_SelectedSection;
std::vector<Feature*> m_Features;
std::vector<std::string> m_ModuleOrder;
std::map<std::string, std::map<std::string, std::vector<Feature*>>> m_FeatureMap;
inline static bool s_IsMenuShowed = false;
bool m_IsBlockingInput;
bool m_IsPrevCursorActive;
bool m_IsProfileConfigurationShowed;
explicit CheatManagerBase();
void DrawExternal() const;
void DrawMenu();
void DrawMenuSection(const std::string& sectionName, const std::vector<Feature*>& features) const;
virtual void DrawProfileGlobalActivities();
virtual void DrawProfileEntryActivities(const std::string&profileName);
virtual void DrawProfileEntry(const std::string& profileName);
virtual void DrawProfileTableHeader();
virtual int GetProfileTableColumnCount();
virtual void DrawProfileConfiguration();
virtual void DrawProfileLine();
virtual void DrawStatus() const;
virtual void DrawInfo();
void DrawFps();
static void DrawNotifications();
void PushFeature(Feature* feature);
void CheckToggles(short key) const;
void ToggleMenuShow();
};
}

View File

@ -0,0 +1,38 @@
#pragma once
#include <string>
namespace cheat
{
struct FeatureGUIInfo
{
std::string name;
std::string moduleName;
bool isGroup;
};
class Feature
{
public:
Feature(Feature const&) = delete;
void operator=(Feature const&) = delete;
// GUI handlers
virtual const FeatureGUIInfo& GetGUIInfo() const = 0;
virtual void DrawMain() = 0;
virtual bool NeedStatusDraw() const { return false; };
virtual void DrawStatus() { };
virtual bool NeedInfoDraw() const { return false; };
virtual void DrawInfo() { };
virtual void DrawExternal() { };
protected:
Feature() { };
};
}

View File

@ -0,0 +1,140 @@
#include <pch.h>
#include "Settings.h"
#include <cheat-base/render/gui-util.h>
#include <cheat-base/render/renderer.h>
#include <cheat-base/cheat/CheatManagerBase.h>
namespace cheat::feature
{
Settings::Settings() : Feature(),
NF(f_MenuKey, "Show Cheat Menu Key", "General", Hotkey(VK_F1)),
NF(f_HotkeysEnabled, "Hotkeys Enabled", "General", true),
NF(f_FontSize, "Font size", "General", 16.0f),
NF(f_StatusMove, "Move Status Window", "General::StatusWindow", true),
NF(f_StatusShow, "Show Status Window", "General::StatusWindow", true),
NF(f_InfoMove, "Move Info Window", "General::InfoWindow", true),
NF(f_InfoShow, "Show Info Window", "General::InfoWindow", true),
NF(f_FpsMove, "Move FPS Indicator", "General::FPS", false),
NF(f_FpsShow, "Show FPS Indicator", "General::FPS", true),
NF(f_NotificationsShow, "Show Notifications", "General::Notify", true),
NF(f_NotificationsDelay, "Notifications Delay", "General::Notify", 500),
NF(f_FileLogging, "File Logging", "General::Logging", false),
NF(f_ConsoleLogging, "Console Logging", "General::Logging", true),
NF(f_FastExitEnable, "Fast Exit", "General::FastExit", false),
NF(f_HotkeyExit, "Hotkeys", "General::FastExit", Hotkey(VK_F12))
{
renderer::SetGlobalFontSize(f_FontSize);
f_HotkeyExit.value().PressedEvent += MY_METHOD_HANDLER(Settings::OnExitKeyPressed);
}
const FeatureGUIInfo& Settings::GetGUIInfo() const
{
static const FeatureGUIInfo info{ "", "Settings", false };
return info;
}
void Settings::DrawMain()
{
BeginGroupPanel("General", ImVec2(-1, 0));
{
ConfigWidget(f_MenuKey, false,
"Key to toggle main menu visibility. Cannot be empty.\n"\
"If you forget this key, you can see or set it in your config file.");
ConfigWidget(f_HotkeysEnabled, "Enable hotkeys.");
if (ConfigWidget(f_FontSize, 1, 8, 64, "Font size for cheat interface."))
{
f_FontSize = std::clamp(f_FontSize.value(), 8, 64);
renderer::SetGlobalFontSize(f_FontSize);
}
}
EndGroupPanel();
BeginGroupPanel("Logging", ImVec2(-1, 0));
{
bool consoleChanged = ConfigWidget(f_ConsoleLogging,
"Enable console for logging information (changes will take effect after relaunch)");
if (consoleChanged && !f_ConsoleLogging)
{
Logger::SetLevel(Logger::Level::None, Logger::LoggerType::ConsoleLogger);
}
bool fileLogging = ConfigWidget(f_FileLogging,
"Enable file logging (changes will take effect after relaunch).\n" \
"A folder in the app directory will be created for logs.");
if (fileLogging && !f_FileLogging)
{
Logger::SetLevel(Logger::Level::None, Logger::LoggerType::FileLogger);
}
}
EndGroupPanel();
BeginGroupPanel("Status Window", ImVec2(-1, 0));
{
ConfigWidget(f_StatusShow);
ConfigWidget(f_StatusMove, "Allow moving of 'Status' window.");
}
EndGroupPanel();
BeginGroupPanel("Info Window", ImVec2(-1, 0));
{
ConfigWidget(f_InfoShow);
ConfigWidget(f_InfoMove, "Allow moving of 'Info' window.");
}
EndGroupPanel();
BeginGroupPanel("FPS indicator", ImVec2(-1, 0));
{
ConfigWidget(f_FpsShow);
ConfigWidget(f_FpsMove, "Allow moving of 'FPS Indicator' window.");
}
EndGroupPanel();
BeginGroupPanel("Show Notifications", ImVec2(-1, 0));
{
ConfigWidget(f_NotificationsShow, "Notifications on the bottom-right corner of the window will be displayed.");
ConfigWidget(f_NotificationsDelay, 1,1,10000, "Delay in milliseconds between notifications.");
}
EndGroupPanel();
BeginGroupPanel("Fast Exit", ImVec2(-1, 0));
{
ConfigWidget("Enabled",
f_FastExitEnable,
"Enable Fast Exit.\n"
);
if (!f_FastExitEnable)
ImGui::BeginDisabled();
ConfigWidget("Key", f_HotkeyExit, true,
"Key to exit the game.");
if (!f_FastExitEnable)
ImGui::EndDisabled();
}
EndGroupPanel();
}
Settings& Settings::GetInstance()
{
static Settings instance;
return instance;
}
void Settings::OnExitKeyPressed()
{
if (!f_FastExitEnable || CheatManagerBase::IsMenuShowed())
return;
ExitProcess(0);
}
}

View File

@ -0,0 +1,44 @@
#pragma once
#include <cheat-base/cheat/Feature.h>
#include <cheat-base/config/config.h>
namespace cheat::feature
{
class Settings : public Feature
{
public:
config::Field<Hotkey> f_MenuKey;
config::Field<bool> f_HotkeysEnabled;
config::Field<int> f_FontSize;
config::Field<bool> f_StatusMove;
config::Field<bool> f_StatusShow;
config::Field<bool> f_InfoMove;
config::Field<bool> f_InfoShow;
config::Field<bool> f_FpsShow;
config::Field<bool> f_FpsMove;
config::Field<bool> f_NotificationsShow;
config::Field<int> f_NotificationsDelay;
config::Field<bool> f_ConsoleLogging;
config::Field<bool> f_FileLogging;
config::Field<bool> f_FastExitEnable;
config::Field<Hotkey> f_HotkeyExit;
static Settings& GetInstance();
const FeatureGUIInfo& GetGUIInfo() const override;
void DrawMain() override;
private:
void OnExitKeyPressed();
Settings();
};
}

View File

@ -0,0 +1,383 @@
#include <pch.h>
#include "config.h"
#include <atomic>
#include <fstream>
#include <SimpleIni.h>
#include <cheat-base/util.h>
namespace config
{
TEvent<> ProfileChanged;
static std::filesystem::path s_Filepath;
static nlohmann::json s_ConfigRoot;
static nlohmann::json s_EmptyJObject = nlohmann::json::object();
// Little speed-up
static nlohmann::json* s_ProfileRoot = nullptr;
static nlohmann::json* s_Profiles = nullptr;
static nlohmann::json* s_SharedRoot = nullptr;
static std::mutex s_ProfileMutex;
static std::string s_ProfileName;
static std::vector<std::string> s_ProfilesNames;
static const int c_SaveDelay = 2000;
static TEvent<>* s_UpdateEvent = nullptr;
static std::atomic<int64_t> s_NextSaveTimestamp = 0;
static std::vector<std::shared_ptr<internal::FieldEntry>> s_Entries;
void LoadFile()
{
std::ifstream fileInput(s_Filepath, std::ios::in);
if (!fileInput.is_open())
{
LOG_DEBUG("Failed to open config file, maybe it's first launch.");
return;
}
try
{
s_ConfigRoot = nlohmann::json::parse(fileInput);
}
catch (nlohmann::json::parse_error& ex)
{
LOG_ERROR("Parse error at byte %llu", ex.byte);
return;
}
}
void UpdateProfilesNames()
{
std::lock_guard _lock(s_ProfileMutex);
s_ProfilesNames.clear();
for (auto& [name, _] : s_Profiles->items())
{
s_ProfilesNames.push_back(name);
}
}
void Initialize(const std::string& filePath)
{
s_ConfigRoot = {};
s_Filepath = filePath;
LoadFile();
if (!s_ConfigRoot.contains("current_profile"))
{
s_ConfigRoot = {
{ "shared", {} },
{ "profiles", {} },
{ "current_profile", ""}
};
}
s_Profiles = &s_ConfigRoot["profiles"];
s_SharedRoot = &s_ConfigRoot["shared"];
if (s_ConfigRoot["current_profile"] == "")
CreateProfile("default");
else
ChangeProfile(s_ConfigRoot["current_profile"]);
UpdateProfilesNames();
}
void OnUpdate();
void SetupUpdate(TEvent<>* updateEvent)
{
s_UpdateEvent = updateEvent;
(*s_UpdateEvent) += FUNCTION_HANDLER(OnUpdate);
}
void UpdateSaveTimestamp()
{
if (!s_UpdateEvent)
return;
if (s_NextSaveTimestamp != 0)
return;
s_NextSaveTimestamp = util::GetCurrentTimeMillisec() + c_SaveDelay;
}
void ResetNotShared()
{
for (auto& entry : s_Entries)
{
if (!entry->IsShared())
entry->Reset();
}
}
nlohmann::json& GetFieldJsonContainer(internal::FieldEntry* field, bool create = false)
{
if (field->GetContainer() != nullptr)
return *field->GetContainer();
nlohmann::json* rootContainer = s_ProfileRoot;
if (field->IsShared())
rootContainer = s_SharedRoot;
auto sectionParts = util::StringSplit("::", field->GetSection());
for (auto& part : sectionParts)
{
if (!rootContainer->contains(part))
{
if (!create)
return s_EmptyJObject;
(*rootContainer)[part] = {};
}
rootContainer = &(*rootContainer)[part];
}
auto& sectionContainer = *rootContainer;
if (!sectionContainer.contains(field->GetName()))
{
if (!create)
return s_EmptyJObject;
sectionContainer[field->GetName()] = {};
}
auto& fieldContainer = sectionContainer[field->GetName()];
field->SetContainer(&fieldContainer);
return fieldContainer;
}
void RemoveFieldContainer(internal::FieldEntry* field, const std::string& section, const std::string& name, bool shared)
{
field->SetContainer(nullptr);
nlohmann::json* rootContainer = s_ProfileRoot;
if (shared)
rootContainer = s_SharedRoot;
auto sectionParts = util::StringSplit("::", section);
std::list<std::pair<std::string, nlohmann::json*>> nodePath;
for (auto& part : sectionParts)
{
if (!(*rootContainer).contains(part))
return;
nodePath.push_front({ part, rootContainer });
rootContainer = &(*rootContainer)[part];
}
if (!rootContainer->contains(name))
return;
rootContainer->erase(name);
for (auto& [key, node] : nodePath)
{
if (!(*node)[key].empty())
break;
node->erase(key);
}
}
void UpdateField(internal::FieldEntry* field)
{
auto& fieldContainer = GetFieldJsonContainer(field);
field->FromJson(fieldContainer);
}
void UpdateNotShared()
{
ResetNotShared();
for (auto& entry : s_Entries)
{
if (!entry->IsShared())
UpdateField(entry.get());
}
}
void LoadField(internal::FieldEntry* field)
{
auto& fieldContainer = GetFieldJsonContainer(field, true);
auto jObject = field->ToJson();
if (jObject.empty())
RemoveFieldContainer(field, field->GetSection(), field->GetName(), field->IsShared());
else
fieldContainer = jObject;
}
void LoadAll()
{
for (auto& entry : s_Entries)
{
LoadField(entry.get());
}
}
void OnFieldChanged(internal::FieldEntry* field)
{
LoadField(field);
Save();
}
void OnFieldMoved(internal::FieldEntry* field, const std::string& oldSection, bool oldShared)
{
RemoveFieldContainer(field, oldSection, field->GetName(), oldShared);
OnFieldChanged(field);
}
void OnFieldReposition(internal::FieldEntry* field, const std::string& oldSection, bool oldShared)
{
field->SetContainer(nullptr);
UpdateField(field);
}
void internal::AddField(std::shared_ptr<FieldEntry> field)
{
s_Entries.push_back(field);
UpdateField(field.get());
field->ChangedEvent += FUNCTION_HANDLER(OnFieldChanged);
field->MovedEvent += FUNCTION_HANDLER(OnFieldMoved);
field->RepositionEvent += FUNCTION_HANDLER(OnFieldReposition);
}
void Refresh()
{
LoadAll();
Save();
}
void SaveInternal()
{
std::ofstream fileOutput(s_Filepath, std::ios::out);
if (!fileOutput.is_open())
{
LOG_DEBUG("Failed to open config file for writing.");
UpdateSaveTimestamp();
return;
}
fileOutput << s_ConfigRoot.dump(4);
fileOutput.close();
}
void Save()
{
if (s_UpdateEvent)
{
UpdateSaveTimestamp();
return;
}
SaveInternal();
}
void OnUpdate()
{
if (s_NextSaveTimestamp > 0 && util::GetCurrentTimeMillisec() > s_NextSaveTimestamp)
{
s_NextSaveTimestamp = 0;
SaveInternal();
}
}
void CreateProfile(const std::string& profileName, bool moveAfterCreate)
{
if (s_Profiles->contains(profileName))
{
if (moveAfterCreate)
ChangeProfile(profileName);
return;
}
(*s_Profiles)[profileName] = {};
UpdateProfilesNames();
if (moveAfterCreate)
ChangeProfile(profileName);
Save();
}
void RemoveProfile(const std::string& profileName)
{
if (!s_Profiles->contains(profileName))
return;
if (s_Profiles->size() == 1)
return;
if (s_ProfileName == profileName)
{
for (auto& [name, value] : s_Profiles->items())
{
if (name != profileName)
{
ChangeProfile(name);
break;
}
}
}
s_Profiles->erase(profileName);
UpdateProfilesNames();
Save();
}
void RenameProfile(const std::string& oldProfileName, const std::string& newProfileName)
{
if (!s_Profiles->contains(oldProfileName) || s_Profiles->contains(newProfileName))
return;
if (s_ProfileName == oldProfileName)
s_ProfileRoot = nullptr;
(*s_Profiles)[newProfileName] = (*s_Profiles)[oldProfileName];
s_Profiles->erase(oldProfileName);
if (s_ProfileRoot == nullptr)
{
for (auto& entry : s_Entries)
{
if (!entry->IsShared())
entry->SetContainer(nullptr);
}
ChangeProfile(newProfileName);
}
UpdateProfilesNames();
Save();
}
void ChangeProfile(const std::string& profileName)
{
if (s_ProfileName == profileName)
return;
if (!s_Profiles->contains(profileName))
return;
std::lock_guard _lock(s_ProfileMutex);
s_ProfileRoot = &(*s_Profiles)[profileName];
s_ProfileName = profileName;
s_ConfigRoot["current_profile"] = profileName;
UpdateNotShared();
Save();
ProfileChanged();
}
std::vector<std::string> const& GetProfiles()
{
return s_ProfilesNames;
}
std::string const& CurrentProfileName()
{
return s_ProfileName;
}
}

View File

@ -0,0 +1,66 @@
#pragma once
#include "Field.h"
#include <vector>
#include <string>
#include "fields/Toggle.h"
#include "fields/Enum.h"
#define NFEX(field, friendName, name, section, defaultValue, shared) field##(config::CreateField<decltype(##field##)::_ValueType>(friendName, name, section, shared, defaultValue))
#define NFEXUP(field, friendName, name, section, shared, ...) field##(config::CreateField<decltype(##field##)::_ValueType>(friendName, name, section, shared, __VA_ARGS__))
#define NFB(field, name, section, defaultValue, shared) NFEX(field, name, config::internal::FixFieldName(#field), section, defaultValue, shared)
#define NFS(field, name, section, defaultValue) NFB(field, name, section, defaultValue, true)
#define NF(field, name, section, defaultValue) NFB(field, name, section, defaultValue, false)
#define NFPB(field, name, section, shared, ...) NFEXUP(field, name, config::internal::FixFieldName(#field), section, shared, __VA_ARGS__)
#define NFPS(field, name, section, ...) NFPB(field, name, section, true, __VA_ARGS__)
#define NFP(field, name, section, ...) NFPB(field, name, section, false, __VA_ARGS__)
namespace config
{
namespace internal
{
template<typename T>
std::vector<T> s_Fields;
void AddField(std::shared_ptr<FieldEntry> field);
inline std::string FixFieldName(const std::string& fieldName)
{
if (fieldName.substr(1, 1) == "_")
return fieldName.substr(2);
return fieldName;
}
}
template<typename T, typename... Args>
Field<T> CreateField(const std::string& friendName, const std::string& name, const std::string& section, bool multiProfile, Args... args)
{
auto newField = Field<T>(friendName, name, section, T(args...), multiProfile);
internal::s_Fields<Field<T>>.push_back(newField);
internal::AddField(newField.entry());
return newField;
}
template<typename T>
std::vector<Field<T>>& GetFields()
{
return internal::s_Fields<Field<T>>;
}
void Initialize(const std::string& filePath);
void SetupUpdate(TEvent<>*);
void Refresh();
void Save();
void CreateProfile(const std::string& profileName, bool moveAfterCreate = true);
void RemoveProfile(const std::string& profileName);
void RenameProfile(const std::string& oldProfileName, const std::string& newProfileName);
void ChangeProfile(const std::string& profileName);
std::vector<std::string> const& GetProfiles();
std::string const& CurrentProfileName();
extern TEvent<> ProfileChanged;
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "internal/FieldSerialize.h"
#include "internal/FieldBase.h"
namespace config
{
template<class T>
class Field : public internal::FieldBase<T>
{
public:
using base = internal::FieldBase<T>;
using base::operator=;
using base::base;
};
}

View File

@ -0,0 +1,68 @@
#pragma once
#include <nlohmann/json.hpp>
#include <cheat-base/Hotkey.h>
#include <imgui.h>
namespace config::converters
{
template<typename T>
inline nlohmann::json ToJson(const T& value)
{
return nlohmann::json(value);
}
template<typename T>
inline void FromJson(T& value, const nlohmann::json& jObject)
{
value = jObject.get<T>();
}
// Here is storing all simple converters json<->class
// ImColor
template<>
inline nlohmann::json ToJson(const ImColor& value)
{
return nlohmann::json((ImU32)value);
}
template<>
inline void FromJson(ImColor& value, const nlohmann::json& jObject)
{
value = { (ImU32)jObject };
}
// Hotkey
template<>
inline nlohmann::json ToJson(const Hotkey& value)
{
auto keys = value.GetKeys();
if (keys.empty())
return {};
if (keys.size() == 1)
return keys[0];
return nlohmann::json(value.GetKeys());
}
template<>
inline void FromJson(Hotkey& value, const nlohmann::json& jObject)
{
if (jObject.is_null() || jObject.empty())
return;
if (jObject.is_number())
{
value = { jObject.get<short>() };
return;
}
value = { jObject.get<std::vector<short>>() };
}
// Enum
}

View File

@ -0,0 +1,100 @@
#pragma once
#include <magic_enum.hpp>
namespace config
{
template<typename T>
class Enum
{
public:
Enum()
{
static_assert(std::is_enum<T>::value, "Must be an enum type");
m_Value = T();
}
Enum(T enumValue)
{
static_assert(std::is_enum<T>::value, "Must be an enum type");
m_Value = enumValue;
}
inline T value() const
{
return m_Value;
}
inline T* pointer() const
{
return const_cast<T*>(&m_Value);
}
inline operator T()
{
return value();
}
inline T* operator&()
{
return pointer();
}
inline uint32_t raw() const
{
return static_cast<uint32_t>(m_Value);
}
inline Enum& operator=(const T& other)
{
static_assert(std::is_enum<T>::value, "Must be an enum type");
m_Value = other;
return *this;
}
inline Enum& operator=(const uint32_t& other)
{
m_Value = static_cast<T>(other);
return *this;
}
private:
T m_Value;
};
//// Okay, close your eyes and don't look at this mess. (Please)
//template <typename K>
//class Field<Enum<K>> : public internal::FieldBase<Enum<K>>
//{
//public:
// using base = internal::FieldBase<Enum<K>>;
// using base::operator=;
// using base::base;
// operator T() const
// {
// return base::value();
// }
//};
}
namespace nlohmann
{
template <typename T>
struct adl_serializer<config::Enum<T>> {
static void to_json(json& j, const config::Enum<T>& enumValue)
{
j = {
{ "name", magic_enum::enum_name(enumValue.value()) },
{ "value", enumValue.raw() }
};
}
static void from_json(const json& j, config::Enum<T>& value)
{
value = j["value"].get<uint32_t>();
}
};
}

View File

@ -0,0 +1,83 @@
#pragma once
#include <cheat-base/Hotkey.h>
#include <cheat-base/config/internal/FieldBase.h>
namespace config
{
template<typename T>
class Toggle
{
public:
bool enabled;
T value;
Toggle(const T& value) : enabled(false), value(value) { }
Toggle(bool enabled) : enabled(enabled), value() { }
Toggle() : enabled(false), value() { }
inline operator bool&()
{
return enabled;
}
inline operator T&()
{
return value;
}
inline bool operator==(const Toggle<T>& rhs)
{
return rhs.enabled == enabled && rhs.value == value;
}
};
// Okay, close your eyes and don't look at this mess. (Please)
template<typename T>
class Field<Toggle<T>> : public internal::FieldBase<Toggle<T>>
{
public:
using base = internal::FieldBase<Toggle<T>>;
using base::operator=;
using base::base;
operator bool() const
{
return base::value();
}
operator T&() const
{
return base::value().value;
}
};
}
namespace nlohmann
{
template <typename T>
struct adl_serializer<config::Toggle<T>> {
static void to_json(json& j, const config::Toggle<T>& toggle)
{
j = {
{ "toggled", toggle.enabled },
{ "value", config::converters::ToJson(toggle.value) }
};
}
static void from_json(const json& j, config::Toggle<T>& toggle)
{
if (j.is_boolean())
{
toggle.enabled = j;
toggle.value = {};
return;
}
toggle.enabled = j["toggled"].get<uint32_t>();
config::converters::FromJson(toggle.value, j.contains("value") ? j["value"] : j["hotkey"]); // Support previously version
}
};
}

View File

@ -0,0 +1,105 @@
#pragma once
#include "FieldEntry.h"
#include "FieldSerialize.h"
namespace config::internal
{
template<typename T>
class FieldBase
{
public:
using _ValueType = T;
explicit FieldBase() : p_Container(nullptr) {}
explicit FieldBase(FieldSerialize<T>* serializeFieldPtr) : p_Container(serializeFieldPtr) { }
explicit FieldBase(const std::shared_ptr<FieldSerialize<T>>& serializeField) : p_Container(serializeField) { }
explicit FieldBase(const std::string friendlyName, const std::string name, const std::string section, T defaultValue, bool multiProfile = false)
: p_Container(std::make_shared<FieldSerialize<T>>(friendlyName, name, section, defaultValue, multiProfile)) { }
std::string name() const
{
return p_Container->GetName();
}
std::string friendName() const
{
return p_Container->GetFriendName();
}
std::string section() const
{
return p_Container->GetSection();
}
bool shared() const
{
return p_Container->IsShared();
}
T& value() const
{
return p_Container->m_Value;
}
T* pointer() const
{
return &p_Container->m_Value;
}
std::shared_ptr<internal::FieldEntry> entry() const
{
return std::static_pointer_cast<FieldEntry>(p_Container);
}
operator T& () const
{
return value();
}
operator T* () const
{
return pointer();
}
void FireChanged() const
{
p_Container->FireChanged();
}
void repos(const std::string& newSection, bool shared = false)
{
p_Container->Reposition(newSection, shared);
}
void move(const std::string& newSection, bool shared = false)
{
p_Container->Move(newSection, shared);
}
FieldBase<T>& operator=(const T& other)
{
p_Container->m_Value = other;
p_Container->FireChanged();
return *this;
}
FieldBase<T>& operator=(std::shared_ptr<FieldSerialize<T>>& other)
{
p_Container = other;
return *this;
}
FieldBase<T>& operator=(FieldSerialize<T>* other)
{
p_Container = std::make_shared<FieldSerialize<T>>(other);
return *this;
}
protected:
std::shared_ptr<FieldSerialize<T>> p_Container;
};
}

View File

@ -0,0 +1,87 @@
#pragma once
#include <string>
#include <cheat-base/events/event.hpp>
#include <nlohmann/json.hpp>
namespace config::internal
{
class FieldEntry
{
public:
FieldEntry(const std::string& friendlyName, const std::string& name, const std::string& sectionName, bool multiProfile = false)
: m_FriendName(friendlyName), m_Name(name), m_Section(sectionName), m_MultiProfile(multiProfile), m_Container(nullptr) {}
TEvent<FieldEntry*> ChangedEvent;
TEvent<FieldEntry*, const std::string&, bool> MovedEvent;
TEvent<FieldEntry*, const std::string&, bool> RepositionEvent;
inline virtual void FireChanged()
{
ChangedEvent(this);
}
virtual nlohmann::json ToJson() = 0;
virtual void FromJson(const nlohmann::json& value) = 0;
virtual void Reset() = 0;
inline bool IsShared() const
{
return m_MultiProfile;
}
inline std::string GetName() const
{
return m_Name;
}
inline std::string GetFriendName() const
{
return m_FriendName;
}
inline std::string GetSection() const
{
return m_Section;
}
inline nlohmann::json* GetContainer() const
{
return m_Container;
}
inline void Reposition(const std::string& newSection, bool shared = false)
{
std::string oldSection = m_Section;
bool oldMultiProfile = m_MultiProfile;
m_Section = newSection;
m_MultiProfile = shared;
RepositionEvent(this, newSection, shared);
}
inline void Move(const std::string& newSection, bool shared = false)
{
std::string oldSection = m_Section;
bool oldMultiProfile = m_MultiProfile;
m_Section = newSection;
m_MultiProfile = shared;
MovedEvent(this, oldSection, oldMultiProfile);
}
inline void SetContainer(nlohmann::json* newContainer)
{
m_Container = nullptr;
}
protected:
std::string m_Name;
std::string m_FriendName;
std::string m_Section;
bool m_MultiProfile;
nlohmann::json* m_Container;
};
}

View File

@ -0,0 +1,42 @@
#pragma once
#include "FieldEntry.h"
#include <cheat-base/config/converters.h>
namespace config::internal
{
template<typename T>
class FieldSerialize : public FieldEntry
{
public:
FieldSerialize(const std::string& friendlyName, const std::string& name, const std::string& sectionName, const T& defaultValue, bool multiProfile = false) :
FieldEntry(friendlyName, name, sectionName, multiProfile), m_Value(defaultValue), m_DefaultValue(defaultValue) { }
nlohmann::json ToJson() override
{
if (m_Value == m_DefaultValue)
return {};
return converters::ToJson(m_Value);
}
void FromJson(const nlohmann::json& jObject) override
{
if (jObject.empty())
{
m_Value = m_DefaultValue;
return;
}
converters::FromJson(m_Value, jObject);
}
void Reset() override
{
m_Value = m_DefaultValue;
}
T m_Value;
T m_DefaultValue;
};
}

View File

@ -0,0 +1,211 @@
// Taken from https://github.com/ZolotovPavel/EventHandling
#pragma once
#include <type_traits>
#include <list>
#include <memory>
#include <shared_mutex>
#include <algorithm>
#include <assert.h>
#include "handlers/abstracteventhandler.hpp"
#include "handlers/eventhandlerptr.h"
#include "handlers/handlercast.hpp"
#include "joins/eventjoinwrapper.hpp"
template<class ...TParams>
struct TypeHelper
{
using TEventHandlerPtr = ::events::handlers::TEventHandlerPtr<TParams...>;
using TEventHandlerIt = typename std::list<TEventHandlerPtr>::const_iterator;
};
namespace joins
{
template<class ...TParams> class HandlerEventJoin;
}
template<class ...TParams>
class IEvent
{
public:
template<class TSome>
::events::EventJoin operator+=( TSome&& some )
{
::events::EventJoin result( *this, std::forward<TSome>( some ) );
result.join();
return result;
}
template<class TSome>
bool operator-=( TSome&& some )
{
return removeHandler(::events::handlers::HandlerCast<TSome>::template cast<TParams...>( some ) );
}
protected:
using TMyEventHandlerPtr = typename TypeHelper<TParams...>::TEventHandlerPtr;
IEvent() {}
virtual bool isHandlerAdded( const TMyEventHandlerPtr& eventHandler ) const = 0;
virtual bool addHandler( TMyEventHandlerPtr eventHandler ) = 0;
virtual bool removeHandler( TMyEventHandlerPtr eventHandler ) = 0;
friend class ::events::joins::HandlerEventJoin<TParams...>;
};
template<class ...TParams>
struct EventCore
{
using TMyHandlerPtr = typename TypeHelper<TParams...>::TEventHandlerPtr;
std::list<TMyHandlerPtr> handlers;
mutable std::shared_mutex coreMutex;
};
template<class ...TParams>
class HandlerRunner
{
using TMyEventCore = EventCore<TParams...>;
using TMyHandlerIt = typename TypeHelper<TParams...>::TEventHandlerIt;
public:
HandlerRunner( TMyEventCore& eventCore ) :
m_eventCore( eventCore ),
currentIt(),
wasRemoving( false )
{
}
void run( TParams... params )
{
m_eventCore.coreMutex.lock_shared();
currentIt = m_eventCore.handlers.begin();
wasRemoving = false;
while( currentIt != m_eventCore.handlers.end() )
{
m_eventCore.coreMutex.unlock_shared();
( *currentIt )->call( params... );
m_eventCore.coreMutex.lock_shared();
if( wasRemoving )
wasRemoving = false;
else
++currentIt;
}
m_eventCore.coreMutex.unlock_shared();
}
TMyHandlerIt currentIt;
mutable bool wasRemoving;
private:
TMyEventCore& m_eventCore;
};
template<class ...TParams>
class TEvent : public IEvent<TParams...>
{
using TMyEventHandlerPtr = typename TypeHelper<TParams...>::TEventHandlerPtr;
using TMyEventHandlerIt = typename TypeHelper<TParams...>::TEventHandlerIt;
using TMyHandlerRunner = HandlerRunner<TParams...>;
public:
TEvent() :
m_core()
{
}
virtual void operator()( TParams... params )
{
TMyHandlerRunner newHandlerRunner( m_core );
m_core.coreMutex.lock_shared();
auto it = m_handlerRunners.insert( m_handlerRunners.end(), &newHandlerRunner );
m_core.coreMutex.unlock_shared();
newHandlerRunner.run( params... );
m_core.coreMutex.lock_shared();
m_handlerRunners.erase( it );
m_core.coreMutex.unlock_shared();
}
protected:
virtual bool isHandlerAdded( const TMyEventHandlerPtr& eventHandler ) const override
{
std::shared_lock<std::shared_mutex> _coreMutexLock( m_core.coreMutex );
return ( findEventHandler( eventHandler ) != m_core.handlers.end() );
}
virtual bool addHandler( TMyEventHandlerPtr eventHandler ) override
{
std::unique_lock<std::shared_mutex> _coreMutexLock( m_core.coreMutex );
if( findEventHandler( eventHandler ) == m_core.handlers.end() )
{
m_core.handlers.push_back( std::move( eventHandler ) );
return true;
}
return false;
}
virtual bool removeHandler( TMyEventHandlerPtr eventHandler ) override
{
std::unique_lock<std::shared_mutex> _coreMutexLock( m_core.coreMutex );
auto it = findEventHandler( eventHandler );
if( it != m_core.handlers.end() )
{
for( TMyHandlerRunner* oneHandlerRunner : m_handlerRunners )
{
if( it == oneHandlerRunner->currentIt )
{
++oneHandlerRunner->currentIt;
oneHandlerRunner->wasRemoving = true;
}
}
m_core.handlers.erase( it );
return true;
}
return false;
}
private:
inline TMyEventHandlerIt findEventHandler( const TMyEventHandlerPtr& eventHandler ) const noexcept
{
return std::find_if( m_core.handlers.cbegin(), m_core.handlers.cend(), [ &eventHandler ]( const TMyEventHandlerPtr& oneHandler )
{
return ( *oneHandler == *eventHandler );
} );
}
EventCore<TParams...> m_core;
std::list<TMyHandlerRunner*> m_handlerRunners;
};
template<class ...TParams>
class TCancelableEvent : public TEvent<TParams..., bool&>
{
using TEventBase = TEvent<TParams..., bool&>;
public:
bool operator()(TParams... params)
{
bool canceled = false;
TEventBase::operator ()(params..., canceled);
return !canceled;
}
};

View File

@ -0,0 +1,34 @@
#pragma once
#include "eventhandlerptr.h"
namespace events::handlers
{
template<class ...TParams>
class AbstractEventHandler
{
using MyType = AbstractEventHandler<TParams...>;
public:
virtual ~AbstractEventHandler() {}
virtual void call(TParams... params) = 0;
bool operator==(const MyType& other) const noexcept
{
return isEquals(other);
}
bool operator!=(const MyType& other) const noexcept
{
return !(*this == other);
}
protected:
AbstractEventHandler() {}
virtual bool isEquals(const MyType& other) const noexcept = 0;
};
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <memory>
namespace events::handlers
{
template<class ...TParams> class AbstractEventHandler;
template<class ...Types>
using TEventHandlerPtr = std::shared_ptr<AbstractEventHandler<Types...>>;
}

View File

@ -0,0 +1,151 @@
#pragma once
#include <memory>
#include <assert.h>
#include "abstracteventhandler.hpp"
#include "helpers/innerholder.hpp"
#include "../helpers/is_equatable.hpp"
namespace events::handlers
{
template<class TFunctor, class ...TParams>
struct IsFunctorParamsCompatible
{
private:
template<class TCheckedFunctor, class ...TCheckedParams>
static constexpr std::true_type exists( decltype( std::declval<TCheckedFunctor>()( std::declval<TCheckedParams>()... ) )* = nullptr ) noexcept;
template<class TCheckedFunctor, class ...TCheckedParams>
static constexpr std::false_type exists( ... ) noexcept;
public:
static constexpr bool value = decltype( exists<TFunctor, TParams...>( nullptr ) )::value;
};
template<class TFunctor> class FunctorHolder;
template<class TFunctor, class ...TParams>
class FunctorEventHandler : public AbstractEventHandler<TParams...>
{
using MyType = FunctorEventHandler<TFunctor, TParams...>;
using TFunctorHolderPtr = std::shared_ptr<FunctorHolder<TFunctor>>;
public:
FunctorEventHandler( TFunctorHolderPtr functorHolder ) :
AbstractEventHandler<TParams...>(),
m_functorHolder( functorHolder )
{
assert( m_functorHolder != nullptr );
}
virtual void call( TParams... params ) override
{
static_assert( IsFunctorParamsCompatible<TFunctor, TParams...>::value, "Event and functor arguments are not compatible" );
m_functorHolder->m_innerHolder.get()( params... );
}
protected:
virtual bool isEquals( const AbstractEventHandler<TParams...>& other ) const noexcept override
{
const MyType* _other = dynamic_cast<const MyType*>( &other );
return ( _other != nullptr && *m_functorHolder == *_other->m_functorHolder );
}
private:
TFunctorHolderPtr m_functorHolder;
};
template<class TEqu, class TEnabled = void>
struct EqualityChecker;
template<class TEquatable>
struct EqualityChecker<TEquatable, typename std::enable_if<is_equatable<TEquatable>::value>::type>
{
static constexpr bool isEquals( const TEquatable& operand1, const TEquatable& operand2 ) noexcept
{
return ( operand1 == operand2 );
}
};
template<class TNonEquatable>
struct EqualityChecker<TNonEquatable, typename std::enable_if<!is_equatable<TNonEquatable>::value>::type>
{
static constexpr bool isEquals( const TNonEquatable& operand1, const TNonEquatable& operand2 ) noexcept
{
return ( &operand1 == &operand2 );
}
};
template<class TFunctor>
class FunctorHolder
{
using MyType = FunctorHolder<TFunctor>;
public:
~FunctorHolder()
{
delete &m_innerHolder;
}
template<class ...TCallParams>
operator TEventHandlerPtr<TCallParams...>()
{
return TEventHandlerPtr<TCallParams...>( new FunctorEventHandler<TFunctor, TCallParams...>( m_me.lock() ) );
}
bool operator==( const MyType& other ) const noexcept
{
return EqualityChecker<TFunctor>::isEquals( m_innerHolder.get(), other.m_innerHolder.get() );
}
bool operator!=( const MyType& other ) const noexcept
{
return !( *this == other );
}
// TFunctor typename is reserved by the enclosing template so need something different
template<class TArgFunctor>
static std::shared_ptr<MyType> create( TArgFunctor&& functor )
{
std::shared_ptr<MyType> result( new MyType( std::forward<TArgFunctor>( functor ) ) );
result->m_me = result;
return result;
}
private:
template<class TArgFunctor>
FunctorHolder( TArgFunctor&& functor ) :
m_innerHolder( createInnerHolder<TFunctor>( std::forward<TArgFunctor>( functor ) ) ),
m_me()
{
}
AbstractInnerHolder<TFunctor>& m_innerHolder;
std::weak_ptr<MyType> m_me;
template<class TArgFunctor, class ...> friend class FunctorEventHandler;
};
template<class TFunctor>
std::shared_ptr<FunctorHolder<typename std::decay<TFunctor>::type>> createFunctorEventHandler( TFunctor&& functor )
{
return FunctorHolder<typename std::decay<TFunctor>::type>::create( std::forward<TFunctor>( functor ) );
}
}
#define FUNCTOR_HANDLER( Functor ) ::events::handlers::createFunctorEventHandler( Functor )
#define LAMBDA_HANDLER( Lambda ) FUNCTOR_HANDLER( Lambda )
#define STD_FUNCTION_HANDLER( StdFunction ) FUNCTOR_HANDLER( StdFunction )
#define FUNCTION_HANDLER( Function ) FUNCTOR_HANDLER( &Function )

View File

@ -0,0 +1,28 @@
#pragma once
#include <memory>
#include "eventhandlerptr.h"
namespace events::handlers
{
template<class TSome>
struct HandlerCast
{
template<class ...Types>
static constexpr TEventHandlerPtr<Types...> cast(TSome& some)
{
return static_cast<TEventHandlerPtr<Types...>>(some);
}
};
template<class TPtr>
struct HandlerCast<std::shared_ptr<TPtr>>
{
template<class ...Types>
static constexpr TEventHandlerPtr<Types...> cast(std::shared_ptr<TPtr> some)
{
return HandlerCast<TPtr>::template cast<Types ...>(*some);
}
};
}

View File

@ -0,0 +1,46 @@
#pragma once
#include "objectsaver.hpp"
namespace events::handlers
{
template<class TBase>
struct AbstractInnerHolder
{
virtual ~AbstractInnerHolder() {}
virtual inline TBase& get() = 0;
inline const TBase& get() const
{
return const_cast<AbstractInnerHolder<TBase>&>(*this).get();
}
};
template<class TBase, class TInner>
struct TInnerHolder : public AbstractInnerHolder<TBase>
{
using TInnerObject = typename ObjectSaver<TInner>::TObject;
TInnerHolder(TInner _inner) :
AbstractInnerHolder<TBase>(),
inner(std::forward<TInner>(_inner))
{ }
virtual inline TBase& get() override
{
return static_cast<TBase&>(inner);
}
TInnerObject inner;
};
template<class TAssignBase, class TArgInner>
AbstractInnerHolder<TAssignBase>& createInnerHolder(TArgInner&& inner)
{
using TAssignInner = decltype(inner);
return *new TInnerHolder<TAssignBase, TAssignInner>(std::forward<TArgInner>(inner));
}
}

View File

@ -0,0 +1,20 @@
#pragma once
namespace events::handlers
{
template<class TSome>
struct ObjectSaver;
template<class LValue>
struct ObjectSaver<LValue&>
{
using TObject = LValue&;
};
template<class RValue>
struct ObjectSaver<RValue&&>
{
using TObject = RValue;
};
}

View File

@ -0,0 +1,130 @@
#pragma once
#include <memory>
#include <assert.h>
#include "abstracteventhandler.hpp"
#include "helpers/innerholder.hpp"
namespace events::handlers
{
template<class TMethodHolder, class ...TParams>
struct IsMethodParamsCompatible
{
private:
template<class TCheckedMethodHolder, class ...TCheckedParams>
static constexpr std::true_type exists( decltype( ( std::declval<TCheckedMethodHolder>().m_innerHolder.get().*std::declval<TCheckedMethodHolder>().m_method )( std::declval<TCheckedParams>()... ) )* = nullptr ) noexcept;
template<class TCheckedMethodHolder, class ...TCheckedParams>
static constexpr std::false_type exists( ... ) noexcept;
public:
static constexpr bool value = decltype( exists<TMethodHolder, TParams...>( nullptr ) )::value;
};
template<class TMethodHolder, class ...TParams>
class MethodEventHandler : public AbstractEventHandler<TParams...>
{
using MyType = MethodEventHandler<TMethodHolder, TParams...>;
using TMethodHolderPtr = std::shared_ptr<TMethodHolder>;
public:
MethodEventHandler( TMethodHolderPtr methodHolder ) :
AbstractEventHandler<TParams...>(),
m_methodHolder( methodHolder )
{
assert( m_methodHolder != nullptr );
}
virtual void call( TParams... params ) override
{
static_assert( IsMethodParamsCompatible<TMethodHolder, TParams...>::value, "Event and method arguments are not compatible" );
( m_methodHolder->m_innerHolder.get().*m_methodHolder->m_method )( params... );
}
protected:
virtual bool isEquals( const AbstractEventHandler<TParams...>& other ) const noexcept override
{
const MyType* _other = dynamic_cast<const MyType*>( &other );
return ( _other != nullptr && *m_methodHolder == *_other->m_methodHolder );
}
private:
TMethodHolderPtr m_methodHolder;
};
template<class TObject, class TResult, class ...TParams>
class MethodHolder
{
using MyType = MethodHolder<TObject, TResult, TParams...>;
using TMethod = TResult( TObject::* )( TParams... );
public:
~MethodHolder()
{
delete &m_innerHolder;
}
template<class ...TCallParams>
operator TEventHandlerPtr<TCallParams...>()
{
return TEventHandlerPtr<TCallParams...>( new MethodEventHandler<MyType, TCallParams...>( m_me.lock() ) );
}
bool operator==( const MyType& other ) const noexcept
{
return ( &m_innerHolder.get() == &other.m_innerHolder.get() && m_method == other.m_method );
}
bool operator!=( const MyType& other ) const noexcept
{
return !( *this == other );
}
// TObject typename is reserved by the enclosing template so need something different
template<class TArgObject>
static std::shared_ptr<MyType> create( TArgObject&& object, TMethod method )
{
std::shared_ptr<MyType> result( new MyType( std::forward<TArgObject>( object ), method ) );
result->m_me = result;
return result;
}
private:
template<class TArgObject>
MethodHolder( TArgObject&& object, TMethod method ) :
m_innerHolder( createInnerHolder<TObject>( std::forward<TArgObject>( object ) ) ),
m_method( method )
{
assert( m_method != nullptr );
}
AbstractInnerHolder<TObject>& m_innerHolder;
TMethod m_method;
std::weak_ptr<MyType> m_me;
template<class TMethodHolder, class ...> friend class MethodEventHandler;
template<class TMethodHolder, class ...> friend struct IsMethodParamsCompatible;
};
template<class TObject, class TResult, class ...TParams>
std::shared_ptr<MethodHolder<std::decay_t<TObject>, TResult, TParams...>> createMethodEventHandler( TObject&& object, TResult( std::decay<TObject>::type::*method )( TParams... ) )
{
return MethodHolder<std::decay_t<TObject>, TResult, TParams...>::create( std::forward<TObject>( object ), method );
}
}
#define METHOD_HANDLER( Object, Method ) ::events::handlers::createMethodEventHandler( Object, &Method )
#define MY_METHOD_HANDLER( Method ) METHOD_HANDLER( *this, Method )

View File

@ -0,0 +1,20 @@
#pragma once
#include <type_traits>
template<class T>
class is_equatable
{
private:
template<class U>
static constexpr std::true_type exists( decltype( std::declval<U>() == std::declval<U>() )* = nullptr ) noexcept;
template<class U>
static constexpr std::false_type exists( ... ) noexcept;
public:
static constexpr bool value = decltype( exists<T>( nullptr ) )::value;
};

View File

@ -0,0 +1,9 @@
#include "abstracteventjoin.h"
namespace events::joins
{
AbstractEventJoin::AbstractEventJoin() {}
AbstractEventJoin::~AbstractEventJoin() {}
}

View File

@ -0,0 +1,20 @@
#pragma once
namespace events::joins
{
class AbstractEventJoin
{
public:
virtual ~AbstractEventJoin();
virtual bool isJoined() const = 0;
virtual bool join() = 0;
virtual bool unjoin() = 0;
protected:
AbstractEventJoin();
};
}

View File

@ -0,0 +1,62 @@
#include "eventjoinwrapper.h"
#include <type_traits>
#include "abstracteventjoin.h"
namespace events::joins
{
constexpr EventJoinWrapper::EventJoinWrapper() noexcept :
m_eventJoin( nullptr )
{ }
EventJoinWrapper::EventJoinWrapper( EventJoinWrapper&& other ) noexcept :
m_eventJoin( std::move( other.m_eventJoin ) )
{ }
EventJoinWrapper::EventJoinWrapper( EventJoinWrapper& other ) noexcept :
m_eventJoin( other.m_eventJoin )
{ }
EventJoinWrapper& EventJoinWrapper::operator=( EventJoinWrapper&& other ) noexcept
{
m_eventJoin = std::move( other.m_eventJoin );
return *this;
}
EventJoinWrapper& EventJoinWrapper::operator=( const EventJoinWrapper& other ) noexcept
{
m_eventJoin = other.m_eventJoin;
return *this;
}
EventJoinWrapper::operator bool() const
{
return isJoined();
}
bool EventJoinWrapper::isAssigned() const
{
return ( m_eventJoin != nullptr );
}
bool EventJoinWrapper::isJoined() const
{
return ( m_eventJoin != nullptr && m_eventJoin->isJoined() );
}
bool EventJoinWrapper::join()
{
return ( m_eventJoin != nullptr ? m_eventJoin->join() : false );
}
bool EventJoinWrapper::unjoin()
{
return ( m_eventJoin != nullptr ? m_eventJoin->unjoin() : false );
}
} // events

View File

@ -0,0 +1,44 @@
#pragma once
#include <memory>
#include "../handlers/eventhandlerptr.h"
template<class ...TParams> class IEvent;
namespace events::joins
{
class AbstractEventJoin;
class EventJoinWrapper
{
public:
template<class TSome, class ...TParams>
inline EventJoinWrapper(IEvent<TParams...>& _event, TSome&& handler);
constexpr EventJoinWrapper() noexcept;
EventJoinWrapper(EventJoinWrapper&& other) noexcept;
EventJoinWrapper(EventJoinWrapper& other) noexcept;
EventJoinWrapper& operator=(EventJoinWrapper&& other) noexcept;
EventJoinWrapper& operator=(const EventJoinWrapper& other) noexcept;
operator bool() const;
bool isAssigned() const;
bool isJoined() const;
bool join();
bool unjoin();
private:
std::shared_ptr<AbstractEventJoin> m_eventJoin;
};
}
namespace events
{
using EventJoin = joins::EventJoinWrapper;
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "eventjoinwrapper.h"
#include "handlereventjoin.h"
#include "../handlers/handlercast.hpp"
namespace events::joins
{
template<class TSome, class ...TParams>
EventJoinWrapper::EventJoinWrapper( IEvent<TParams...>& _event, TSome&& handler ) :
m_eventJoin( std::make_shared<HandlerEventJoin<TParams...>>( _event, ::events::handlers::HandlerCast<TSome>::template cast<TParams...>( handler ) ) )
{ }
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "abstracteventjoin.h"
#include "../handlers/eventhandlerptr.h"
template<class ...TParams> class IEvent;
namespace events::joins
{
template<class ...TParams>
class HandlerEventJoin : public AbstractEventJoin
{
public:
HandlerEventJoin(IEvent<TParams...>& _event, ::events::handlers::TEventHandlerPtr<TParams...> handler) :
AbstractEventJoin(),
m_event(_event),
m_handler(handler)
{ }
virtual inline bool isJoined() const override;
virtual inline bool join() override;
virtual inline bool unjoin() override;
private:
IEvent<TParams...>& m_event;
::events::handlers::TEventHandlerPtr<TParams...> m_handler;
};
}

View File

@ -0,0 +1,26 @@
#pragma once
#include "handlereventjoin.h"
#include "../event.hpp"
namespace events::joins
{
template<class ...TParams>
bool HandlerEventJoin<TParams...>::isJoined() const
{
return m_event.isHandlerAdded(m_handler);
}
template<class ...TParams>
bool HandlerEventJoin<TParams...>::join()
{
return m_event.addHandler(m_handler);
}
template<class ...TParams>
bool HandlerEventJoin<TParams...>::unjoin()
{
return m_event.removeHandler(m_handler);
}
}

View File

@ -0,0 +1,10 @@
#include <pch.h>
#include "globals.h"
namespace events
{
TCancelableEvent<short> KeyUpEvent{};
TCancelableEvent<HWND, UINT, WPARAM, LPARAM> WndProcEvent {};
TEvent<> RenderEvent {};
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <Windows.h>
#include <cheat-base/events/event.hpp>
namespace events
{
extern TCancelableEvent<short> KeyUpEvent;
extern TCancelableEvent<HWND, UINT, WPARAM, LPARAM> WndProcEvent;
extern TEvent<> RenderEvent;
}

View File

@ -0,0 +1,54 @@
#include <pch.h>
#include "ImageLoader.h"
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <cheat-base/util.h>
#include <cheat-base/render/backend/dx11-hook.h>
#include <cheat-base/ResourceLoader.h>
std::optional<ImageLoader::ImageData> ImageLoader::GetImage(const std::string& imageName, const char* type)
{
if (s_Textures.count(imageName) > 0)
return s_Textures[imageName];
LPBYTE pDestination;
DWORD size;
bool loadResult = ResourceLoader::LoadEx(imageName.c_str(), type, pDestination, size);
if (!loadResult)
{
// LOG_WARNING("Failed to load image: %s from resources", imageName);
return {};
}
// Load from disk into a raw RGBA buffer
int image_width = 0;
int image_height = 0;
unsigned char* image_data = stbi_load_from_memory(pDestination, size, &image_width, &image_height, NULL, 4);
if (image_data == NULL)
{
// LOG_WARNING("Failed to convert image '%s' to RGBA by 'stb_image.h'", imageName.c_str());
return {};
}
ImageLoader::ImageData imageData = {};
ID3D11ShaderResourceView* new_texture = NULL;
int width, height;
bool textureResult = backend::LoadTextureFromMemory(image_data, image_width, image_height,
reinterpret_cast<ID3D11ShaderResourceView**>(&imageData.textureID), &width, &height);
stbi_image_free(image_data);
if (!textureResult)
{
// LOG_WARNING("Failed to load texture by DX11 for image: %s", imageName.c_str());
return {};
}
imageData.size.x = width;
imageData.size.y = height;
s_Textures[imageName] = imageData;
return imageData;
}

View File

@ -0,0 +1,24 @@
#pragma once
#include <string>
#include <map>
#include <optional>
#include <Windows.h>
#include <imgui.h>
class ImageLoader
{
public:
struct ImageData
{
ImTextureID textureID;
ImVec2 size;
};
static std::optional<ImageData> GetImage(const std::string& imageName, const char* imageType = "PNG");
private:
inline static std::map<std::string, ImageData> s_Textures {};
};

View File

@ -0,0 +1,141 @@
#include <pch.h>
#include "dx11-hook.h"
#include <cstdio>
#include <cheat-base/HookManager.h>
#pragma comment(lib, "D3dcompiler.lib")
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "winmm.lib")
// D3X HOOK DEFINITIONS
typedef HRESULT(__stdcall* IDXGISwapChainPresent)(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags);
// Definition of WndProc Hook. Its here to avoid dragging dependencies on <windows.h> types.
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
static IDXGISwapChainPresent fnIDXGISwapChainPresent;
static ID3D11Device* pDevice = nullptr;
static HRESULT __stdcall Present(IDXGISwapChain* pChain, const UINT SyncInterval, const UINT Flags)
{
static BOOL g_bInitialised = false;
// Main D3D11 Objects
static ID3D11DeviceContext* pContext = nullptr;
if (!g_bInitialised) {
pChain->GetDevice(__uuidof(pDevice), reinterpret_cast<void**>(&pDevice));
pDevice->GetImmediateContext(&pContext);
DXGI_SWAP_CHAIN_DESC sd;
pChain->GetDesc(&sd);
backend::DX11Events::InitializeEvent(sd.OutputWindow, pDevice, pContext, pChain);
g_bInitialised = true;
}
// render function
backend::DX11Events::RenderEvent(pContext);
return CALL_ORIGIN(Present, pChain, SyncInterval, Flags);
}
static IDXGISwapChainPresent findDirect11Present()
{
const HWND hWnd = GetForegroundWindow();
IDXGISwapChain* pSwapChain;
constexpr D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0;
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
swapChainDesc.BufferCount = 1;
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.OutputWindow = hWnd;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.Windowed = TRUE; //((GetWindowLong(hWnd, GWL_STYLE) & WS_POPUP) != 0) ? FALSE : TRUE;
swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
// Main D3D11 Objects
ID3D11DeviceContext* pContext = nullptr;
ID3D11Device* pDevice = nullptr;
if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_WARP, NULL, NULL, &featureLevel, 1,
D3D11_SDK_VERSION, &swapChainDesc, &pSwapChain, &pDevice, NULL, &pContext)) &&
FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, &featureLevel, 1,
D3D11_SDK_VERSION, &swapChainDesc, &pSwapChain, &pDevice, NULL, &pContext)))
{
return nullptr;
}
const DWORD_PTR* pSwapChainVtable = reinterpret_cast<DWORD_PTR*>(pSwapChain);
pSwapChainVtable = reinterpret_cast<DWORD_PTR*>(pSwapChainVtable[0]);
auto swapChainPresent = reinterpret_cast<IDXGISwapChainPresent>(pSwapChainVtable[8]);
pDevice->Release();
pContext->Release();
pSwapChain->Release();
return swapChainPresent;
}
void backend::InitializeDX11Hooks()
{
LOG_DEBUG("Initializing D3D11 hook: started.");
fnIDXGISwapChainPresent = findDirect11Present();
if (fnIDXGISwapChainPresent == nullptr)
{
LOG_ERROR("Failed to find 'Present' function for D3D11.");
return;
}
LOG_DEBUG("SwapChain Present: %p", fnIDXGISwapChainPresent);
HookManager::install(fnIDXGISwapChainPresent, Present);
LOG_DEBUG("Initializing D3D11 hook: done.");
}
bool backend::LoadTextureFromMemory(LPBYTE image_data, int image_width, int image_height, ID3D11ShaderResourceView** out_srv, int* out_width, int* out_height)
{
if (pDevice == nullptr)
return false;
// Create texture
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Width = image_width;
desc.Height = image_height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
ID3D11Texture2D* pTexture = NULL;
D3D11_SUBRESOURCE_DATA subResource;
subResource.pSysMem = image_data;
subResource.SysMemPitch = desc.Width * 4;
subResource.SysMemSlicePitch = 0;
pDevice->CreateTexture2D(&desc, &subResource, &pTexture);
// Create texture view
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = desc.MipLevels;
srvDesc.Texture2D.MostDetailedMip = 0;
pDevice->CreateShaderResourceView(pTexture, &srvDesc, out_srv);
pTexture->Release();
*out_width = image_width;
*out_height = image_height;
return true;
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <Windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <cheat-base/events/event.hpp>
namespace backend
{
void InitializeDX11Hooks();
// Thanks to https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples#example-for-directx11-users
bool LoadTextureFromMemory(LPBYTE image_data, int image_width, int image_height, ID3D11ShaderResourceView** out_srv, int* out_width, int* out_height);
class DX11Events
{
public:
inline static TEvent<ID3D11DeviceContext*> RenderEvent{};
inline static TEvent<HWND, ID3D11Device*, ID3D11DeviceContext*, IDXGISwapChain*> InitializeEvent{};
};
}

View File

@ -0,0 +1,740 @@
#include <pch.h>
#include "gui-util.h"
#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui_internal.h"
#include <imgui.h>
#include <misc/cpp/imgui_stdlib.h>
#include <cheat-base/util.h>
#include <shellapi.h>
void ShowHelpText(const char* text)
{
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(text);
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
void HelpMarker(const char* desc)
{
ImGui::TextDisabled("(?)");
if (ImGui::IsItemHovered())
ShowHelpText(desc);
}
bool InputHotkey(const char* label, Hotkey* hotkey, bool clearable)
{
char hotkeyBuffer[50];
auto hotkeyString = std::string(*hotkey);
memcpy(hotkeyBuffer, hotkeyString.c_str(), hotkeyString.size() + 1);
bool changed = false;
if (clearable) {
char labelBuffer[128];
std::snprintf(labelBuffer, 128, "Clear ## %s_1", label);
if (ImGui::Button(labelBuffer, ImVec2(75, 0)))
{
*hotkey = Hotkey();
changed = true;
}
ImGui::SameLine();
}
changed = ImGui::HotkeyWidget(label, *hotkey, ImVec2(200, 0)) || changed;
return changed;
}
float CalcWidth(const std::string_view& view)
{
ImGuiContext& g = *GImGui;
return ImGui::CalcTextSize(view.data()).x + g.Style.FramePadding.x * 2.0f + 25.0f;
}
#define END_TYPE_WIDGET() \
if (desc != nullptr) { ImGui::SameLine(); HelpMarker(desc); } \
return result;
#define END_CONFIG_WIDGET() if (result) field.FireChanged(); return result;
bool TypeWidget(const char* label, bool& value, const char* desc)
{
bool result = ImGui::Checkbox(label, &value);
END_TYPE_WIDGET();
}
bool TypeWidget(const char* label, int& value, int step, int start, int end, const char* desc)
{
bool result = false;
if (start == end)
result = ImGui::InputInt(label, &value, step);
else
result = ImGui::DragInt(label, &value, (float)step, start, end);
END_TYPE_WIDGET();
}
bool TypeWidget(const char* label, float& value, float step, float start, float end, const char* desc)
{
bool result = false;
if (start == end)
result = ImGui::InputFloat(label, &value, step);
else
result = ImGui::DragFloat(label, &value, step, start, end);
END_TYPE_WIDGET();
}
bool TypeWidget(const char* label, Hotkey& value, bool clearable, const char* desc)
{
bool result = InputHotkey(label, &value, clearable);
END_TYPE_WIDGET();
}
bool TypeWidget(const char* label, std::string& value, const char* desc)
{
bool result = ImGui::InputText(label, &value);
END_TYPE_WIDGET();
}
bool TypeWidget(const char* label, ImColor& value, const char* desc)
{
bool result = ImGui::ColorEdit4(label, reinterpret_cast<float*>(&value));
END_TYPE_WIDGET();
}
bool TypeWidget(const char* label, config::Toggle<Hotkey>& value, const char* desc, bool hotkey)
{
bool result = hotkey ? InputHotkey(label, &value.value, true) : ImGui::Checkbox(label, &value.enabled);
END_TYPE_WIDGET();
}
bool ConfigWidget(const char* label, config::Field<bool>& field, const char* desc)
{
bool result = TypeWidget(label, field.value(), desc);
END_CONFIG_WIDGET();
}
bool ConfigWidget(const char* label, config::Field<int>& field, int step, int start, int end, const char* desc)
{
bool result = TypeWidget(label, field.value(), step, start, end, desc);
END_CONFIG_WIDGET();
}
bool ConfigWidget(const char* label, config::Field<float>& field, float step, float start, float end, const char* desc)
{
bool result = TypeWidget(label, field.value(), step, start, end, desc);
END_CONFIG_WIDGET();
}
bool ConfigWidget(const char* label, config::Field<Hotkey>& field, bool clearable, const char* desc)
{
bool result = TypeWidget(label, field.value(), clearable, desc);
END_CONFIG_WIDGET();
}
bool ConfigWidget(const char* label, config::Field<std::string>& field, const char* desc)
{
bool result = TypeWidget(label, field.value(), desc);
END_CONFIG_WIDGET();
}
bool ConfigWidget(const char* label, config::Field<ImColor>& field, const char* desc /*= nullptr*/)
{
bool result = TypeWidget(label, field.value(), desc);
END_CONFIG_WIDGET();
}
bool ConfigWidget(const char* label, config::Field<config::Toggle<float>>& field, float step, float start, float end,
const char* desc, bool hotkey)
{
ImGui::PushID(&label);
bool result = TypeWidget("", field.value().enabled);
ImGui::SameLine();
result |= TypeWidget(label, field.value().value, step, start, end, desc);
ImGui::PopID();
END_CONFIG_WIDGET();
}
bool ConfigWidget(const char* label, config::Field<config::Toggle<Hotkey>>& field, const char* desc /*= nullptr*/, bool hotkey /*= false*/)
{
bool result = TypeWidget(label, field.value(), desc, hotkey);
END_CONFIG_WIDGET();
}
bool ConfigWidget(config::Field<bool>& field, const char* desc)
{
return ConfigWidget(field.friendName().c_str(), field, desc);
}
bool ConfigWidget(config::Field<int>& field, int step, int start, int end, const char* desc)
{
return ConfigWidget(field.friendName().c_str(), field, step, start, end, desc);
}
bool ConfigWidget(config::Field<float>& field, float step, float start, float end, const char* desc)
{
return ConfigWidget(field.friendName().c_str(), field, step, start, end, desc);
}
bool ConfigWidget(config::Field<Hotkey>& field, bool clearable, const char* desc)
{
return ConfigWidget(field.friendName().c_str(), field, clearable, desc);
}
bool ConfigWidget(config::Field<std::string>& field, const char* desc)
{
return ConfigWidget(field.friendName().c_str(), field, desc);
}
bool ConfigWidget(config::Field<ImColor>& field, const char* desc /*= nullptr*/)
{
return ConfigWidget(field.friendName().c_str(), field, desc);
}
bool ConfigWidget(config::Field<config::Toggle<float>>& field, float step, float start, float end, const char* desc)
{
return ConfigWidget(field.friendName().c_str(), field, step, start, end, desc);
}
bool ConfigWidget(config::Field<config::Toggle<Hotkey>>& field, const char* desc /*= nullptr*/, bool hotkey /*= false*/)
{
return ConfigWidget(field.friendName().c_str(), field, desc, hotkey);
}
#undef ShowDesc
// https://github.com/ocornut/imgui/issues/1496#issuecomment-655048353
struct GroupPanelInfo
{
ImRect labelRect;
ImRect selectRect;
};
static ImVector<GroupPanelInfo> s_GroupPanelLabelStack;
bool GroupPanelIsOpen(ImGuiID id)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiStorage* storage = window->DC.StateStorage;
return storage->GetInt(id, 1) != 0;
}
void GroupPanelSetOpen(ImGuiID id, bool open)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGuiStorage* storage = window->DC.StateStorage;
storage->SetInt(id, open ? 1 : 0);
}
bool BeginGroupPanel(const char* name, const ImVec2& size, bool node, SelectData* selectData)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow;
ImGui::PushID(name);
ImGui::BeginGroup();
auto cursorPos = ImGui::GetCursorScreenPos();
auto itemSpacing = ImGui::GetStyle().ItemSpacing;
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
auto frameHeight = ImGui::GetFrameHeight();
ImGui::BeginGroup();
ImVec2 effectiveSize = size;
if (size.x < 0.0f)
effectiveSize.x = ImGui::GetContentRegionAvail().x;
else
effectiveSize.x = size.x;
ImGui::Dummy(ImVec2(effectiveSize.x, 0.0f));
ImVec2 startPos = window->DC.CursorPos;
ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
ImGui::SameLine(0.0f, 0.0f);
ImGui::BeginGroup();
ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
ImGui::SameLine(0.0f, 0.0f);
ImGui::TextUnformatted(name);
auto labelMin = ImGui::GetItemRectMin();
auto labelMax = ImGui::GetItemRectMax();
ImGui::SameLine(0.0f, 0.0f);
ImVec2 selectMin = {};
ImVec2 selectMax = {};
if (selectData != nullptr)
{
bool useText = true;
const char* selectAll = "Select all";
auto textSize = ImGui::CalcTextSize(selectAll);
auto spaceSize = ImVec2(effectiveSize.x - textSize.x - 35.0f - labelMax.x + startPos.x, 0.0f);
if (spaceSize.x <= 0)
{
spaceSize = ImVec2(effectiveSize.x - 35.0f - labelMax.x + startPos.x, 0.0f);
useText = false;
}
ImGui::Dummy(spaceSize);
ImGui::SameLine(0.0f, 0.0f);
selectData->changed = ImGui::Checkbox(useText ? selectAll : "", &selectData->toggle);
selectMin = ImGui::GetItemRectMin();
selectMax = ImGui::GetItemRectMax();
}
ImGui::SameLine(0.0f, 0.0f);
ImGui::Dummy(ImVec2(0.0, frameHeight + itemSpacing.y));
if (node)
{
labelMin.x = startPos.x;
const ImVec2 text_size = ImGui::CalcTextSize(name);
const ImGuiID id = window->GetID(name);
bool isOpen = GroupPanelIsOpen(id);
bool hovered;
bool toggled = ImGui::ButtonBehavior({ labelMin, labelMax }, id, &hovered, nullptr, ImGuiButtonFlags_PressedOnClick);
if (toggled)
{
isOpen = !isOpen;
GroupPanelSetOpen(id, isOpen);
}
const ImU32 text_col = ImGui::GetColorU32(ImGuiCol_Text);
ImGui::RenderArrow(window->DrawList, { cursorPos.x, cursorPos.y + text_size.y * 0.15f }, text_col,
isOpen ? ImGuiDir_Down : ImGuiDir_Right, 0.7f);
if (!isOpen)
{
ImGui::PopStyleVar(2);
ImGui::EndGroup();
ImGui::EndGroup();
ImGui::EndGroup();
ImGui::PopID();
return false;
}
}
ImGui::BeginGroup();
//ImGui::GetWindowDrawList()->AddRect(labelMin, labelMax, IM_COL32(255, 0, 255, 255));
ImGui::PopStyleVar(2);
#if IMGUI_VERSION_NUM >= 17301
ImGui::GetCurrentWindow()->ContentRegionRect.Max.x -= frameHeight * 0.5f;
ImGui::GetCurrentWindow()->WorkRect.Max.x -= frameHeight * 0.5f;
ImGui::GetCurrentWindow()->InnerRect.Max.x -= frameHeight * 0.5f;
#else
ImGui::GetCurrentWindow()->ContentsRegionRect.Max.x -= frameHeight * 0.5f;
#endif
ImGui::GetCurrentWindow()->Size.x -= frameHeight;
auto itemWidth = ImGui::CalcItemWidth();
ImGui::PushItemWidth(ImMax(0.0f, itemWidth - frameHeight));
s_GroupPanelLabelStack.push_back({ ImRect(labelMin, labelMax) , ImRect(selectMin, selectMax)});
return true;
}
void EndGroupPanel()
{
ImGui::PopItemWidth();
auto itemSpacing = ImGui::GetStyle().ItemSpacing;
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
auto frameHeight = ImGui::GetFrameHeight();
ImGui::EndGroup();
//ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(0, 255, 0, 64), 4.0f);
ImGui::EndGroup();
ImGui::SameLine(0.0f, 0.0f);
ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
ImGui::Dummy(ImVec2(0.0, frameHeight - frameHeight * 0.5f - itemSpacing.y));
ImGui::EndGroup();
auto itemMin = ImGui::GetItemRectMin();
auto itemMax = ImGui::GetItemRectMax();
//ImGui::GetWindowDrawList()->AddRectFilled(itemMin, itemMax, IM_COL32(255, 0, 0, 64), 4.0f);
auto& info = s_GroupPanelLabelStack.back();
s_GroupPanelLabelStack.pop_back();
ImVec2 halfFrame = ImVec2(frameHeight * 0.25f, frameHeight) * 0.5f;
ImRect frameRect = ImRect(itemMin + halfFrame, itemMax - ImVec2(halfFrame.x, 0.0f));
auto& labelRect = info.labelRect;
labelRect.Min.x -= itemSpacing.x;
labelRect.Max.x += itemSpacing.x;
bool hasSelect = info.selectRect.Min.x != 0;
if (!hasSelect)
{
for (int i = 0; i < 3; ++i)
{
switch (i)
{
// left half-plane
case 0: ImGui::PushClipRect(ImVec2(-FLT_MAX, -FLT_MAX), ImVec2(labelRect.Min.x, FLT_MAX), true); break;
// right half-plane
case 1: ImGui::PushClipRect(ImVec2(labelRect.Max.x, -FLT_MAX), ImVec2(FLT_MAX, FLT_MAX), true); break;
// bottom
case 2: ImGui::PushClipRect(ImVec2(labelRect.Min.x, labelRect.Max.y), ImVec2(labelRect.Max.x, FLT_MAX), true); break;
}
ImGui::GetWindowDrawList()->AddRect(
frameRect.Min, frameRect.Max,
ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Border)),
halfFrame.x);
ImGui::PopClipRect();
}
}
else
{
auto& selectRect = info.selectRect;
selectRect.Min.x -= itemSpacing.x;
selectRect.Max.x += itemSpacing.x;
for (int i = 0; i < 5; ++i)
{
switch (i)
{
// left half-plane
case 0: ImGui::PushClipRect(ImVec2(-FLT_MAX, -FLT_MAX), ImVec2(labelRect.Min.x, FLT_MAX), true); break;
// label - select
case 1: ImGui::PushClipRect(ImVec2(labelRect.Max.x, -FLT_MAX), ImVec2(selectRect.Min.x, FLT_MAX), true); break;
// bottom label
case 2: ImGui::PushClipRect(ImVec2(labelRect.Min.x, labelRect.Max.y), ImVec2(labelRect.Max.x, FLT_MAX), true); break;
// bottom select
case 3: ImGui::PushClipRect(ImVec2(selectRect.Min.x, selectRect.Max.y), ImVec2(selectRect.Max.x, FLT_MAX), true); break;
// right hand-plane
case 4: ImGui::PushClipRect(ImVec2(selectRect.Max.x, -FLT_MAX), ImVec2(FLT_MAX, FLT_MAX), true); break;
}
ImGui::GetWindowDrawList()->AddRect(
frameRect.Min, frameRect.Max,
ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Border)),
halfFrame.x);
ImGui::PopClipRect();
}
}
ImGui::PopStyleVar(2);
#if IMGUI_VERSION_NUM >= 17301
ImGui::GetCurrentWindow()->ContentRegionRect.Max.x += frameHeight * 0.5f;
ImGui::GetCurrentWindow()->WorkRect.Max.x += frameHeight * 0.5f;
ImGui::GetCurrentWindow()->InnerRect.Max.x += frameHeight * 0.5f;
#else
ImGui::GetCurrentWindow()->ContentsRegionRect.Max.x += frameHeight * 0.5f;
#endif
ImGui::GetCurrentWindow()->Size.x += frameHeight;
ImGui::Dummy(ImVec2(0.0f, 0.0f));
ImGui::EndGroup();
ImGui::PopID();
}
void AddUnderLine(ImColor col_)
{
ImVec2 min = ImGui::GetItemRectMin();
ImVec2 max = ImGui::GetItemRectMax();
min.y = max.y;
ImGui::GetWindowDrawList()->AddLine(min, max, col_, 1.0f);
}
void TextURL(const char* name_, const char* URL_, bool SameLineBefore_, bool SameLineAfter_)
{
if (SameLineBefore_) { ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); }
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]);
ImGui::Text(name_);
ImGui::PopStyleColor();
if (ImGui::IsItemHovered())
{
if (ImGui::IsMouseClicked(0))
{
ShellExecute(0, 0, URL_, 0, 0, SW_SHOW);
}
AddUnderLine(ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]);
ImGui::SetTooltip("Open in browser\n%s", URL_);
}
else
{
AddUnderLine(ImGui::GetStyle().Colors[ImGuiCol_Button]);
}
if (SameLineAfter_) { ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); }
}
bool operator&(OutlineSide lhs, OutlineSide rhs) {
return
static_cast<std::underlying_type<OutlineSide>::type>(lhs) &
static_cast<std::underlying_type<OutlineSide>::type>(rhs);
}
void DrawTextWithOutline(ImDrawList* drawList, ImFont* font, float fontSize, const ImVec2& screenPos,
const char* text, const ImColor& textColor, float outlineThickness, OutlineSide sides, const ImColor& outlineColor)
{
if (outlineThickness == 0.0f)
{
drawList->AddText(font, fontSize, screenPos, outlineColor, text);
}
else
{
if (sides & OutlineSide::Left)
drawList->AddText(font, fontSize,
{ screenPos.x - outlineThickness, screenPos.y }, outlineColor, text);
if (sides & OutlineSide::Right)
drawList->AddText(font, fontSize,
{ screenPos.x + outlineThickness, screenPos.y }, outlineColor, text);
if (sides & OutlineSide::Bottom)
drawList->AddText(font, fontSize,
{ screenPos.x, screenPos.y - outlineThickness }, outlineColor, text);
if (sides & OutlineSide::Top)
drawList->AddText(font, fontSize,
{ screenPos.x, screenPos.y + outlineThickness }, outlineColor, text);
}
drawList->AddText(font, fontSize, screenPos, textColor, text);
}
void DrawTextWithOutline(ImDrawList* drawList, const ImVec2& screenPos, const char* text, const ImColor& textColor,
float outlineThickness, OutlineSide sides, const ImColor& outlineColor)
{
DrawTextWithOutline(drawList, nullptr, 0.0f, screenPos, text, textColor, outlineThickness, sides, outlineColor);
}
// Modified version of: https://github.com/spirthack/CSGOSimple/blob/master/CSGOSimple/UI.cpp#L287
bool ImGui::HotkeyWidget(const char* label, Hotkey& hotkey, const ImVec2& size)
{
// Init ImGui
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
ImGuiIO& io = g.IO;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
const ImVec2 item_size = ImGui::CalcItemSize(size, ImGui::CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0f);
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + item_size);
const ImRect total_bb(window->DC.CursorPos, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
ImGui::ItemSize(total_bb, style.FramePadding.y);
if (!ImGui::ItemAdd(total_bb, id))
return false;
const bool focus_requested = (ImGui::GetItemStatusFlags() & ImGuiItemStatusFlags_FocusedByTabbing) != 0 || g.NavActivateInputId == id;
const bool hovered = ImGui::ItemHoverable(frame_bb, id);
if (hovered)
{
ImGui::SetHoveredID(id);
g.MouseCursor = ImGuiMouseCursor_TextInput;
}
static Hotkey _initHotkey;
static Hotkey _currHotkey;
static Hotkey _prevHotkey;
const bool user_clicked = hovered && io.MouseClicked[0];
if (focus_requested || user_clicked)
{
if (g.ActiveId != id)
{
memset(io.MouseDown, 0, sizeof(io.MouseDown));
memset(io.KeysDown, 0, sizeof(io.KeysDown));
_initHotkey = hotkey;
_currHotkey = Hotkey();
_prevHotkey = Hotkey();
}
ImGui::SetActiveID(id, window);
ImGui::FocusWindow(window);
}
else if (io.MouseClicked[0] && g.ActiveId == id)
{
ImGui::ClearActiveID();
}
bool valueChanged = false;
if (g.ActiveId == id)
{
if (ImGui::IsKeyPressed(ImGuiKey_Escape))
{
ImGui::ClearActiveID();
if (hotkey != _initHotkey)
{
hotkey = _initHotkey;
valueChanged = true;
}
}
else
{
ImGui::NavMoveRequestCancel();
auto newHotkey = Hotkey::GetPressedHotkey();
if (newHotkey.IsEmpty() && !_currHotkey.IsEmpty())
{
ImGui::ClearActiveID();
valueChanged = false;
}
else if (newHotkey - _prevHotkey)
{
_currHotkey = newHotkey;
hotkey = newHotkey;
valueChanged = true;
}
_prevHotkey = newHotkey;
}
}
// Render
// Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is Set 'buf' might still be the old value. We Set buf to NULL to prevent accidental usage from now on.
char buf_display[128] = "Empty";
const ImU32 frame_col = ImGui::GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
ImGui::RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
if ((g.ActiveId == id && !_currHotkey.IsEmpty()) || g.ActiveId != id)
strcpy_s(buf_display, static_cast<std::string>(hotkey).c_str());
else if (g.ActiveId == id)
strcpy_s(buf_display, "<Press a key>");
const ImRect clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + item_size.x, frame_bb.Min.y + item_size.y); // Not using frame_bb.Max because we have adjusted size
ImVec2 render_pos = frame_bb.Min + style.FramePadding;
ImGui::RenderTextClipped(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding, buf_display, NULL, NULL, style.ButtonTextAlign, &clip_rect);
//RenderTextClipped(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding, buf_display, NULL, NULL, GetColorU32(ImGuiCol_Text), style.ButtonTextAlign, &clip_rect);
//draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, NULL, 0.0f, &clip_rect);
if (label_size.x > 0)
ImGui::RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
return valueChanged;
}
// https://github.com/ocornut/imgui/issues/3798
float ImGui::CalcContrastRatio(const ImU32& backgroundColor, const ImU32& foreGroundColor)
{
// real code https://www.w3.org/TR/WCAG20/#relativeluminancedef
/*const auto colBG = ImGui::ColorConvertU32ToFloat4(backgroundColor);
const auto colFG = ImGui::ColorConvertU32ToFloat4(foreGroundColor);
float lumBG = 0.2126 * colBG.x + 0.7152 * colBG.y + 0.0722 * colBG.z;
float lumFG = 0.2126 * colFG.x + 0.7152 * colFG.y + 0.0722 * colFG.z;
return (ImMax(lumBG, lumFG) + 0.05) / (ImMin(lumBG, lumFG) + 0.05);*/
float sa0 = ((backgroundColor >> IM_COL32_A_SHIFT) & 0xFF);
float sa1 = ((foreGroundColor >> IM_COL32_A_SHIFT) & 0xFF);
static float sr = 0.2126f / 255.0f;
static float sg = 0.7152f / 255.0f;
static float sb = 0.0722f / 255.0f;
const float contrastRatio =
(sr * sa0 * ((backgroundColor >> IM_COL32_R_SHIFT) & 0xFF) +
sg * sa0 * ((backgroundColor >> IM_COL32_G_SHIFT) & 0xFF) +
sb * sa0 * ((backgroundColor >> IM_COL32_B_SHIFT) & 0xFF) + 0.05f) /
(sr * sa1 * ((foreGroundColor >> IM_COL32_R_SHIFT) & 0xFF) +
sg * sa1 * ((foreGroundColor >> IM_COL32_G_SHIFT) & 0xFF) +
sb * sa1 * ((foreGroundColor >> IM_COL32_B_SHIFT) & 0xFF) + 0.05f);
if (contrastRatio < 1.0f)
return 1.0f / contrastRatio;
return contrastRatio;
}
ImColor ImGui::CalcContrastColor(const ImColor& foreground, float maxContrastRatio, const ImColor& background, const ImColor& inverted)
{
return ImGui::CalcContrastRatio(background, foreground) < maxContrastRatio ? inverted : background;
}
bool ImGui::PushStyleColorWithContrast(ImU32 backGroundColor, ImGuiCol foreGroundColor, ImU32 invertedColor, float maxContrastRatio)
{
const float contrastRatio = CalcContrastRatio(backGroundColor, GetColorU32(foreGroundColor));
if (contrastRatio < maxContrastRatio)
{
ImGui::PushStyleColor(foreGroundColor, invertedColor);
return true;
}
return false;
}
static std::string nameBuffer;
void ImGui::OpenRenamePopup(const std::string& initName)
{
ImGui::OpenPopup("RenamePopup");
if (IsRenamePopupOpened())
nameBuffer = initName;
}
bool ImGui::IsRenamePopupOpened()
{
return ImGui::IsPopupOpen("RenamePopup");
}
bool ImGui::DrawRenamePopup(std::string& out)
{
if (ImGui::BeginPopup("RenamePopup", ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::Text("To save press `Enter`.\nTo close without saving press `Esc`.");
if (!ImGui::IsAnyItemActive() && !ImGui::IsMouseClicked(0))
ImGui::SetKeyboardFocusHere(0);
ImGui::InputText("Name", &nameBuffer);
bool changed = false;
if (ImGui::IsKeyPressed(ImGuiKey_Enter, false))
{
changed = true;
out = nameBuffer;
ImGui::CloseCurrentPopup();
}
if (ImGui::IsKeyPressed(ImGuiKey_Escape, false))
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
return changed;
}
return false;
}

View File

@ -0,0 +1,163 @@
#pragma once
#include <imgui.h>
#include <filesystem>
#include <cheat-base/config/config.h>
#include <cheat-base/Hotkey.h>
#include <cheat-base/config/fields/Toggle.h>
#include <cheat-base/config/fields/Enum.h>
#define BLOCK_FOCUS()
bool TypeWidget(const char* label, bool& value, const char* desc = nullptr);
bool TypeWidget(const char* label, int& value, int step = 1, int start = 0, int end = 0, const char* desc = nullptr);
bool TypeWidget(const char* label, float& value, float step = 1.0F, float start = 0, float end = 0, const char* desc = nullptr);
bool TypeWidget(const char* label, Hotkey& value, bool clearable = true, const char* desc = nullptr);
bool TypeWidget(const char* label, std::string& value, const char* desc = nullptr);
bool TypeWidget(const char* label, ImColor& value, const char* desc = nullptr);
bool TypeWidget(const char* label, config::Toggle<Hotkey>& value, const char* desc = nullptr, bool hotkey = false);
bool ConfigWidget(const char* label, config::Field<bool>& field, const char* desc = nullptr);
bool ConfigWidget(const char* label, config::Field<int>& field, int step = 1, int start = 0, int end = 0, const char* desc = nullptr);
bool ConfigWidget(const char* label, config::Field<float>& field, float step = 1.0F, float start = 0, float end = 0, const char* desc = nullptr);
bool ConfigWidget(const char* label, config::Field<Hotkey>& field, bool clearable = true, const char* desc = nullptr);
bool ConfigWidget(const char* label, config::Field<std::string>& field, const char* desc = nullptr);
bool ConfigWidget(const char* label, config::Field<ImColor>& field, const char* desc = nullptr);
template<typename T>
bool ConfigWidget(const char* label, config::Field<config::Toggle<T>>& field, const char* desc = nullptr)
{
ImGui::PushID(&field);
bool result = TypeWidget("", field.value().enabled);
ImGui::SameLine();
result |= TypeWidget(label, field.value().value, desc);
ImGui::PopID();
if (result)
field.FireChanged();
return result;
}
bool ConfigWidget(const char* label, config::Field<config::Toggle<float>>& field, float step = 1.0F, float start = 0, float end = 0, const char* desc = nullptr, bool hotkey = false);
bool ConfigWidget(const char* label, config::Field<config::Toggle<Hotkey>>& field, const char* desc = nullptr, bool hotkey = false);
bool ConfigWidget(config::Field<bool>& field, const char* desc = nullptr);
bool ConfigWidget(config::Field<int>& field, int step = 1, int start = 0, int end = 0, const char* desc = nullptr);
bool ConfigWidget(config::Field<float>& field, float step = 1.0F, float start = 0, float end = 0, const char* desc = nullptr);
bool ConfigWidget(config::Field<Hotkey>& field, bool clearable = true, const char* desc = nullptr);
bool ConfigWidget(config::Field<std::string>& field, const char* desc = nullptr);
bool ConfigWidget(config::Field<ImColor>& field, const char* desc = nullptr);
template<typename T>
bool ConfigWidget(config::Field<config::Toggle<T>>& field, const char* desc = nullptr)
{
return ConfigWidget(field.friendName().c_str(), field, desc);
}
bool ConfigWidget(config::Field<config::Toggle<float>>& field, float step = 1.0F, float start = 0, float end = 0, const char* desc = nullptr);
bool ConfigWidget(config::Field<config::Toggle<Hotkey>>& field, const char* desc = nullptr, bool hotkey = false);
void ShowHelpText(const char* text);
void HelpMarker(const char* desc);
bool InputHotkey(const char* label, Hotkey* hotkey, bool clearable);
// Thanks to https://gist.github.com/dougbinks/ef0962ef6ebe2cadae76c4e9f0586c69
void AddUnderLine(ImColor col_);
void TextURL(const char* name_, const char* URL_, bool SameLineBefore_, bool SameLineAfter_);
enum class OutlineSide : uint32_t
{
Left = 1,
Right = 2,
Top = 4,
Bottom = 8,
All = Left | Right | Top | Bottom
};
bool operator&(OutlineSide lhs, OutlineSide rhs);
void DrawTextWithOutline(ImDrawList* drawList, ImFont* font, float fontSize, const ImVec2& screenPos, const char* text, const ImColor& textColor,
float outlineThickness = 0.0f, OutlineSide sides = OutlineSide::All, const ImColor& outlineColor = ImColor(0.0f, 0.0f, 0.0f));
void DrawTextWithOutline(ImDrawList* drawList, const ImVec2& screenPos, const char* text, const ImColor& textColor,
float outlineThickness = 0.0f, OutlineSide sides = OutlineSide::All, const ImColor& outlineColor = ImColor(0.0f, 0.0f, 0.0f));
struct SelectData
{
bool toggle;
bool changed;
};
bool BeginGroupPanel(const char* name, const ImVec2& size = ImVec2(-1, 0), bool node = false, SelectData* selectData = nullptr);
void EndGroupPanel();
namespace ImGui
{
bool HotkeyWidget(const char* label, Hotkey& hotkey, const ImVec2& size = ImVec2(0, 0));
float CalcContrastRatio(const ImU32& backgroundColor, const ImU32& foreGroundColor);
ImColor CalcContrastColor(const ImColor& foreground, float maxContrastRatio = 2.0f, const ImColor& background = ImColor(1.0f, 1.0f, 1.0f), const ImColor& inverted = ImColor(0.0f, 0.0f, 0.0f));
bool PushStyleColorWithContrast(ImU32 backGroundColor, ImGuiCol foreGroundColor, ImU32 invertedColor, float maxContrastRatio);
void OpenRenamePopup(const std::string& initName);
bool IsRenamePopupOpened();
bool DrawRenamePopup(std::string& out);
}
float CalcWidth(const std::string_view& view);
template <typename T>
float GetMaxEnumWidth()
{
constexpr auto names = magic_enum::enum_names<T>();
auto maxComboName = std::max_element(names.begin(), names.end(),
[](const auto& a, const auto& b) { return CalcWidth(a) < CalcWidth(b); });
return CalcWidth(*maxComboName);
}
template <typename T>
bool ComboEnum(const char* label, T* currentValue)
{
auto name = magic_enum::enum_name(*currentValue);
auto& current = *currentValue;
bool result = false;
static auto width = GetMaxEnumWidth<T>();
ImGui::SetNextItemWidth(width);
if (ImGui::BeginCombo(label, name.data()))
{
for (auto& entry : magic_enum::enum_entries<T>())
{
bool is_selected = (name == entry.second);
if (ImGui::Selectable(entry.second.data(), is_selected))
{
current = entry.first;
result = true;
}
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
return result;
}
template <typename T>
bool ConfigWidget(const char* label, config::Field<config::Enum<T>>& field, const char* desc = nullptr)
{
bool result = false;
if (ComboEnum(label, &field.value()))
{
field.FireChanged();
result = true;
}
if (desc != nullptr) { ImGui::SameLine(); HelpMarker(desc); };
return result;
}
template <typename T>
bool ConfigWidget(config::Field<config::Enum<T>>& field, const char* desc = nullptr)
{
return ConfigWidget(field.friendName().c_str(), field, desc);
}

View File

@ -0,0 +1,291 @@
#include <pch.h>
#include "renderer.h"
#include <backends/imgui_impl_dx11.h>
#include <backends/imgui_impl_win32.h>
#include <cheat-base/util.h>
#include <cheat-base/render/backend/dx11-hook.h>
#include <cheat-base/ResourceLoader.h>
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
namespace renderer
{
struct Data
{
LPBYTE data;
DWORD size;
};
static std::unordered_set<void*> _inputLockers;
static float _globalFontSize = 16.0f;
static bool _isCustomFontLoaded = false;
static constexpr int _fontSizeStep = 1;
static constexpr int _fontSizeMax = 64;
static constexpr int _fontsCount = _fontSizeMax / _fontSizeStep;
static std::array<ImFont*, _fontsCount> _fonts;
static Data _customFontData {};
static WNDPROC OriginalWndProcHandler;
static ID3D11RenderTargetView* mainRenderTargetView;
static void OnRender(ID3D11DeviceContext* pContext);
static void OnDX11Initialize(HWND window, ID3D11Device* pDevice, ID3D11DeviceContext* pContext, IDXGISwapChain* pChain);
void Init(LPBYTE fontData, DWORD fontDataSize)
{
_customFontData = { fontData, fontDataSize };
LOG_DEBUG("Initialize IMGui...");
backend::DX11Events::RenderEvent += FUNCTION_HANDLER(OnRender);
backend::DX11Events::InitializeEvent += FUNCTION_HANDLER(OnDX11Initialize);
backend::InitializeDX11Hooks();
}
void SetInputLock(void* id, bool value)
{
if (value)
AddInputLocker(id);
else
RemoveInputLocker(id);
}
void AddInputLocker(void* id)
{
if (_inputLockers.count(id) == 0)
_inputLockers.insert(id);
}
void RemoveInputLocker(void* id)
{
if (_inputLockers.count(id) > 0)
_inputLockers.erase(id);
}
bool IsInputLocked()
{
return _inputLockers.size() > 0;
}
ImFont* GetFontBySize(float fontSize)
{
if (!_isCustomFontLoaded)
{
ImGuiIO& io = ImGui::GetIO();
return io.FontDefault;
}
int fontSizeInt = static_cast<int>(fontSize);
int fontIndex = fontSizeInt / _fontSizeStep +
(fontSizeInt % _fontSizeStep > (_fontSizeStep / 2) ? 1 : 0) - 1;
fontIndex = std::clamp(fontIndex, 0, _fontsCount - 1);
return _fonts[fontIndex];
}
float GetScaleByFontSize(float fontSize)
{
if (!_isCustomFontLoaded)
{
ImGuiIO& io = ImGui::GetIO();
return fontSize / io.FontDefault->FontSize;
}
int fontSizeInt = static_cast<int>(fontSize);
int fontIndex = fontSizeInt / _fontSizeStep;
int fontAligned = fontIndex * _fontSizeStep +
((fontSizeInt % _fontSizeStep) > _fontSizeStep / 2 ? _fontSizeStep : 0);
fontAligned = std::clamp(fontAligned, _fontSizeStep, _fontSizeMax);
return fontSize / static_cast<float>(fontAligned);
}
void SetGlobalFontSize(float globalFontSize)
{
_globalFontSize = globalFontSize;
}
float GetGlobalFontSize()
{
return _globalFontSize;
}
static void LoadCustomFont()
{
if (_customFontData.data == nullptr)
return;
for (int i = 0; i < _fontsCount; i++)
{
ImGuiIO& io = ImGui::GetIO();
auto newFont = io.Fonts->AddFontFromMemoryTTF(_customFontData.data, _customFontData.size, (i + 1) * _fontSizeStep);
if (newFont == nullptr)
return;
_fonts[i] = newFont;
}
_isCustomFontLoaded = true;
}
static void SetupImGuiStyle();
static LRESULT CALLBACK hWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static void OnDX11Initialize(HWND window, ID3D11Device* pDevice, ID3D11DeviceContext* pContext, IDXGISwapChain* pChain)
{
LOG_DEBUG("ImGUI: DirectX11 backend initialized successfully.");
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
LoadCustomFont();
SetupImGuiStyle();
//Set OriginalWndProcHandler to the Address of the Original WndProc function
OriginalWndProcHandler = reinterpret_cast<WNDPROC>(SetWindowLongPtr(window, GWLP_WNDPROC,
reinterpret_cast<LONG_PTR>(hWndProc)));
ImGui_ImplWin32_Init(window);
ImGui_ImplDX11_Init(pDevice, pContext);
ID3D11Texture2D* pBackBuffer;
pChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<LPVOID*>(&pBackBuffer));
pDevice->CreateRenderTargetView(pBackBuffer, nullptr, &mainRenderTargetView);
pBackBuffer->Release();
io.SetPlatformImeDataFn = nullptr; // F**king bug take 4 hours of my life
}
static void OnRender(ID3D11DeviceContext* pContext)
{
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.FontDefault = GetFontBySize(_globalFontSize);
ImGui::NewFrame();
events::RenderEvent();
ImGui::EndFrame();
ImGui::Render();
pContext->OMSetRenderTargets(1, &mainRenderTargetView, nullptr);
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
}
static LRESULT CALLBACK hWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
ImGuiIO& io = ImGui::GetIO();
POINT mPos;
GetCursorPos(&mPos);
ScreenToClient(hWnd, &mPos);
ImGui::GetIO().MousePos.x = mPos.x;
ImGui::GetIO().MousePos.y = mPos.y;
ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam);
if (!events::WndProcEvent(hWnd, uMsg, wParam, lParam))
return true;
short key;
bool keyUpEvent = true;
switch (uMsg)
{
case WM_LBUTTONUP:
key = VK_LBUTTON;
break;
case WM_RBUTTONUP:
key = VK_RBUTTON;
break;
case WM_MBUTTONUP:
key = VK_MBUTTON;
break;
case WM_XBUTTONUP:
key = GET_XBUTTON_WPARAM(wParam);
break;
case WM_KEYUP:
key = wParam;
break;
default:
keyUpEvent = false;
break;
}
bool canceled = false;
if (keyUpEvent)
canceled = !events::KeyUpEvent(key);
if (IsInputLocked() || canceled)
return true;
return CallWindowProc(OriginalWndProcHandler, hWnd, uMsg, wParam, lParam);
}
static void SetupImGuiStyle()
{
ImGui::GetStyle().FrameRounding = 4.0f;
ImGui::GetStyle().GrabRounding = 4.0f;
ImVec4* colors = ImGui::GetStyle().Colors;
colors[ImGuiCol_Text] = ImVec4(0.95f, 0.96f, 0.98f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.17f, 0.21f, 0.24f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.12f, 0.15f, 0.14f, 1.00f);
colors[ImGuiCol_ChildBg] = ImVec4(0.12f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.09f, 0.11f, 0.94f);
colors[ImGuiCol_Border] = ImVec4(0.15f, 0.22f, 0.25f, 1.00f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.20f, 0.20f, 0.20f, 0.00f);
colors[ImGuiCol_FrameBg] = ImVec4(0.18f, 0.25f, 0.27f, 1.00f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.15f, 0.19f, 0.24f, 1.00f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.19f, 0.22f, 0.24f, 1.00f);
colors[ImGuiCol_TitleBg] = ImVec4(0.14f, 0.18f, 0.22f, 0.65f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.13f, 0.16f, 0.19f, 1.00f);
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.15f, 0.15f, 0.15f, 0.51f);
colors[ImGuiCol_MenuBarBg] = ImVec4(0.12f, 0.14f, 0.18f, 1.00f);
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.11f, 0.11f, 0.11f, 0.39f);
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.53f, 0.37f, 0.37f, 1.00f);
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.49f, 0.23f, 0.23f, 1.00f);
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.56f, 0.10f, 0.10f, 1.00f);
colors[ImGuiCol_CheckMark] = ImVec4(0.65f, 0.74f, 0.86f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.35f, 0.47f, 0.68f, 1.00f);
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.68f, 0.80f, 1.00f, 1.00f);
colors[ImGuiCol_Button] = ImVec4(0.14f, 0.19f, 0.24f, 1.00f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.27f, 0.30f, 0.44f, 1.00f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.28f, 0.29f, 0.41f, 1.00f);
colors[ImGuiCol_Header] = ImVec4(0.00f, 0.00f, 0.00f, 0.24f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.12f, 0.16f, 0.20f, 0.80f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.00f, 0.44f, 0.92f, 1.00f);
colors[ImGuiCol_Separator] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.31f, 0.45f, 0.60f, 0.78f);
colors[ImGuiCol_SeparatorActive] = ImVec4(0.42f, 0.57f, 0.75f, 1.00f);
colors[ImGuiCol_ResizeGrip] = ImVec4(0.64f, 0.79f, 0.98f, 0.25f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.65f, 0.75f, 0.87f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.43f, 0.55f, 0.70f, 0.95f);
colors[ImGuiCol_Tab] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_TabHovered] = ImVec4(0.59f, 0.59f, 0.59f, 0.80f);
colors[ImGuiCol_TabActive] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_TabUnfocused] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
}
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <Windows.h>
#include <cheat-base/events/event.hpp>
#include <imgui.h>
namespace renderer
{
void Init(LPBYTE pFontData, DWORD dFontDataSize);
// Font sizing
ImFont* GetFontBySize(float fontSize);
float GetScaleByFontSize(float fontSize);
void SetGlobalFontSize(float globalFontSize);
float GetGlobalFontSize();
// Input lock
void SetInputLock(void* id, bool value);
void AddInputLocker(void* id);
void RemoveInputLocker(void* id);
bool IsInputLocked();
}

View File

@ -0,0 +1,87 @@
#pragma once
#include <mutex>
#include <queue>
#include <optional>
template<typename T>
class SafeQueue
{
public:
SafeQueue() = default;
SafeQueue(const SafeQueue<T>&) = delete;
SafeQueue& operator=(const SafeQueue<T>&) = delete;
SafeQueue(SafeQueue<T>&& other) {
std::lock_guard<std::mutex> lock(mutex_);
queue_ = std::move(other.queue_);
}
virtual ~SafeQueue() { }
unsigned long size() const {
std::lock_guard<std::mutex> lock(mutex_);
return queue_.size();
}
std::optional<T> pop()
{
std::lock_guard<std::mutex> lock(mutex_);
if (queue_.empty()) {
return {};
}
T tmp = queue_.front();
queue_.pop();
return tmp;
}
void push(const T& item) {
std::lock_guard<std::mutex> lock(mutex_);
queue_.push(item);
}
private:
std::queue<T> queue_;
mutable std::mutex mutex_;
bool empty() const {
return queue_.empty();
}
};
template <class T>
class SafeValue
{
public:
SafeValue(T initValue) : value(initValue) {}
SafeValue(const SafeValue<T>&) = delete;
SafeValue& operator=(const SafeValue<T>&) = delete;
T GetValue() const
{
std::lock_guard<std::mutex> lock(mutex_);
return value;
}
void SetValue(const T& newValue)
{
std::lock_guard<std::mutex> lock(mutex_);
value = newValue;
}
SafeValue& operator=(const T& newValue)
{
SetValue(newValue);
return *this;
}
operator T() const
{
return GetValue();
}
private:
T value;
mutable std::mutex mutex_;
};

View File

@ -0,0 +1,264 @@
#include <pch.h>
#include <framework.h>
#include "util.h"
#include <Windows.h>
#include <commdlg.h>
#include <shtypes.h>
#include <shobjidl_core.h>
#include <shlobj_core.h>
#include <sstream>
#include <string>
#include <iomanip>
#include <codecvt>
#include <filesystem>
namespace util
{
std::string GetLastErrorAsString(DWORD errorId /*= 0*/)
{
//Get the error message ID, if any.
DWORD errorMessageID = errorId == 0 ? ::GetLastError() : errorId;
if (errorMessageID == 0)
{
return std::string(); //No error message has been recorded
}
LPSTR messageBuffer = nullptr;
//Ask Win32 to give us the string version of that message ID.
//The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be).
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
//Copy the error message into a std::string.
std::string message(messageBuffer, size);
//Free the Win32's string's buffer.
LocalFree(messageBuffer);
return message;
}
std::string to_hex_string(uint8_t* barray, int length)
{
if (barray == nullptr || length == 0)
return std::string();
std::stringstream stream;
for (size_t i = 0; i < length; i++)
stream << std::setfill('0') << std::setw(2) << std::hex << (int)barray[i];
return stream.str();
}
bool IsLittleEndian()
{
unsigned int i = 1;
char* c = (char*)&i;
return (*c);
}
std::optional<std::string> SelectDirectory(const char* title)
{
auto currPath = std::filesystem::current_path();
if (!SUCCEEDED(CoInitialize(nullptr)))
return {};
IFileDialog* pfd;
if (!SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd))))
return {};
const size_t titleSize = strlen(title) + 1;
wchar_t* wcTitle = new wchar_t[titleSize];
mbstowcs(wcTitle, title, titleSize);
DWORD dwOptions;
IShellItem* psi;
if (!SUCCEEDED(pfd->GetOptions(&dwOptions)) ||
!SUCCEEDED(pfd->SetOptions(dwOptions | FOS_PICKFOLDERS)) ||
!SUCCEEDED(pfd->SetTitle(wcTitle)) ||
!SUCCEEDED(pfd->Show(NULL)) ||
!SUCCEEDED(pfd->GetResult(&psi)))
{
pfd->Release();
return {};
}
WCHAR* folderName;
if (!SUCCEEDED(psi->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &folderName)))
{
pfd->Release();
psi->Release();
return {};
}
pfd->Release();
psi->Release();
std::filesystem::current_path(currPath);
std::u16string u16(reinterpret_cast<const char16_t*>(folderName));
return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(u16);
}
std::optional<std::string> SelectFile(const char* filter, const char* title)
{
auto currPath = std::filesystem::current_path();
// common dialog box structure, setting all fields to 0 is important
OPENFILENAME ofn = { 0 };
TCHAR szFile[260] = { 0 };
// Initialize remaining fields of OPENFILENAME structure
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFile = szFile;
ofn.nMaxFile = sizeof(szFile);
ofn.lpstrFilter = filter;
ofn.lpstrTitle = title;
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
std::optional<std::string> result = {};
if (GetOpenFileName(&ofn) == TRUE)
result = std::string(szFile);
std::filesystem::current_path(currPath);
return result;
}
std::optional<std::string> GetOrSelectPath(CSimpleIni& ini, const char* section, const char* name, const char* friendName, const char* filter)
{
auto savedPath = ini.GetValue(section, name);
if (savedPath != nullptr)
return std::string(savedPath);
LOG_DEBUG("%s path not found. Please point to it manually.", friendName);
auto titleStr = string_format("Select %s", friendName);
auto selectedPath = filter == nullptr ? SelectDirectory(titleStr.c_str()) : SelectFile(filter, titleStr.c_str());
if (!selectedPath)
return {};
ini.SetValue(section, name, selectedPath->c_str());
return selectedPath;
}
int64_t GetCurrentTimeMillisec()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
std::vector<std::string> StringSplit(const std::string& delimiter, const std::string& content)
{
std::vector<std::string> tokens;
size_t pos = 0;
size_t prevPos = 0;
std::string token;
while ((pos = content.find(delimiter, prevPos)) != std::string::npos) {
token = content.substr(prevPos, pos - prevPos);
tokens.push_back(token);
prevPos = pos + delimiter.length();
}
tokens.push_back(content.substr(prevPos));
return tokens;
}
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(BYTE c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(BYTE const* buf, unsigned int bufLen) {
std::string ret;
int i = 0;
int j = 0;
BYTE char_array_3[3];
BYTE char_array_4[4];
while (bufLen--) {
char_array_3[i++] = *(buf++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; (i < 4); i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for (j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while ((i++ < 3))
ret += '=';
}
return ret;
}
std::vector<BYTE> base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
BYTE char_array_4[4], char_array_3[3];
std::vector<BYTE> ret;
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i == 4) {
for (i = 0; i < 4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret.push_back(char_array_3[i]);
i = 0;
}
}
if (i) {
for (j = i; j < 4; j++)
char_array_4[j] = 0;
for (j = 0; j < 4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret.push_back(char_array_3[j]);
}
return ret;
}
}

View File

@ -0,0 +1,79 @@
#pragma once
#include <string>
#include <stdexcept>
#include <memory>
#include <optional>
#include <vector>
#include <cheat-base/Logger.h>
#include <SimpleIni.h>
#define LOG_LAST_ERROR(fmt, ...) util::LogLastError(__FILE__, __LINE__, fmt, __VA_ARGS__)
#define UPDATE_DELAY(delay) \
static ULONGLONG s_LastUpdate = 0; \
ULONGLONG currentTime = GetTickCount64();\
if (s_LastUpdate + (delay) > currentTime) \
return; \
s_LastUpdate = currentTime;
#define UPDATE_DELAY_VAR(type, name, delay) \
static type name = {}; \
static ULONGLONG s_LastUpdate = 0; \
ULONGLONG currentTime = GetTickCount64();\
if (s_LastUpdate + (delay) > currentTime) \
return name; \
s_LastUpdate = currentTime;
namespace util
{
std::optional<std::string> SelectFile(const char* filter, const char* title);
std::optional<std::string> SelectDirectory(const char* title);
std::optional<std::string> GetOrSelectPath(CSimpleIni& ini, const char* section, const char* name, const char* friendName, const char* filter);
std::string GetLastErrorAsString(DWORD errorId = 0);
int64_t GetCurrentTimeMillisec();
std::vector<std::string> StringSplit(const std::string& delimiter, const std::string& content);
std::string to_hex_string(uint8_t* barray, int length);
bool IsLittleEndian();
std::string base64_encode(BYTE const* buf, unsigned int bufLen);
std::vector<BYTE> base64_decode(std::string const&);
template<typename ... Args>
std::string string_format(const std::string& format, Args ... args)
{
int size_s = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1; // Extra space for '\0'
if (size_s <= 0) { throw std::runtime_error("Error during formatting."); }
auto size = static_cast<size_t>(size_s);
auto buf = std::make_unique<char[]>(size);
std::snprintf(buf.get(), size, format.c_str(), args ...);
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
}
template<typename ... Args>
void LogLastError(const char* filepath, int line, const char* fmt, Args ... args)
{
auto errorId = ::GetLastError();
auto newFmt = string_format("%s. Error: %d %s", fmt, errorId, GetLastErrorAsString(errorId).c_str());
Logger::Log(Logger::Level::Error, filepath, line, newFmt.c_str(), args ...);
}
template<class T>
static T ReadMapped(void* data, int offset, bool littleEndian = false)
{
char* cData = (char*)data;
T result = {};
if (IsLittleEndian() != littleEndian)
{
for (int i = 0; i < sizeof(T); i++)
((char*)&result)[i] = cData[offset + sizeof(T) - i - 1];
return result;
}
memcpy_s(&result, sizeof(result), cData + offset, sizeof(result));
return result;
}
}

Binary file not shown.

1227
cheat-base/vendor/detours/detours.h vendored Normal file

File diff suppressed because it is too large Load Diff

27
cheat-base/vendor/detours/detver.h vendored Normal file
View File

@ -0,0 +1,27 @@
//////////////////////////////////////////////////////////////////////////////
//
// Common version parameters.
//
// Microsoft Research Detours Package, Version 4.0.1
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#define _USING_V110_SDK71_ 1
#include "winver.h"
#if 0
#include <windows.h>
#include <detours.h>
#else
#ifndef DETOURS_STRINGIFY
#define DETOURS_STRINGIFY_(x) #x
#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x)
#endif
#define VER_FILEFLAGSMASK 0x3fL
#define VER_FILEFLAGS 0x0L
#define VER_FILEOS 0x00040004L
#define VER_FILETYPE 0x00000002L
#define VER_FILESUBTYPE 0x00000000L
#endif
#define VER_DETOURS_BITS DETOURS_STRINGIFY(DETOURS_BITS)

1
cheat-base/vendor/fmt vendored Submodule

Submodule cheat-base/vendor/fmt added at 86e27ccb41

1
cheat-base/vendor/imgui vendored Submodule

Submodule cheat-base/vendor/imgui added at af916cdf1a

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,357 @@
// imgui-notify by patrickcjk
// https://github.com/patrickcjk/imgui-notify
#ifndef IMGUI_NOTIFY
#define IMGUI_NOTIFY
#pragma once
#include <vector>
#include <string>
#include "font_awesome_5.h"
#include "fa_solid_900.h"
#define NOTIFY_MAX_MSG_LENGTH 4096 // Max message content length
#define NOTIFY_PADDING_X 20.f // Bottom-left X padding
#define NOTIFY_PADDING_Y 20.f // Bottom-left Y padding
#define NOTIFY_PADDING_MESSAGE_Y 10.f // Padding Y between each message
#define NOTIFY_FADE_IN_OUT_TIME 150 // Fade in and out duration
#define NOTIFY_DEFAULT_DISMISS 3000 // Auto dismiss after X ms (default, applied only of no data provided in constructors)
#define NOTIFY_OPACITY 1.0f // 0-1 Toast opacity
#define NOTIFY_TOAST_FLAGS ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing
// Comment out if you don't want any separator between title and content
#define NOTIFY_USE_SEPARATOR
#define NOTIFY_INLINE inline
#define NOTIFY_NULL_OR_EMPTY(str) (!str ||! strlen(str))
#define NOTIFY_FORMAT(fn, format, ...) if (format) { va_list args; va_start(args, format); fn(format, args, __VA_ARGS__); va_end(args); }
typedef int ImGuiToastType;
typedef int ImGuiToastPhase;
typedef int ImGuiToastPos;
enum ImGuiToastType_
{
ImGuiToastType_None,
ImGuiToastType_Success,
ImGuiToastType_Warning,
ImGuiToastType_Error,
ImGuiToastType_Info,
ImGuiToastType_COUNT
};
enum ImGuiToastPhase_
{
ImGuiToastPhase_FadeIn,
ImGuiToastPhase_Wait,
ImGuiToastPhase_FadeOut,
ImGuiToastPhase_Expired,
ImGuiToastPhase_COUNT
};
enum ImGuiToastPos_
{
ImGuiToastPos_TopLeft,
ImGuiToastPos_TopCenter,
ImGuiToastPos_TopRight,
ImGuiToastPos_BottomLeft,
ImGuiToastPos_BottomCenter,
ImGuiToastPos_BottomRight,
ImGuiToastPos_Center,
ImGuiToastPos_COUNT
};
class ImGuiToast
{
private:
ImGuiToastType type = ImGuiToastType_None;
char title[NOTIFY_MAX_MSG_LENGTH];
char content[NOTIFY_MAX_MSG_LENGTH];
int dismiss_time = NOTIFY_DEFAULT_DISMISS;
uint64_t creation_time = 0;
private:
// Setters
NOTIFY_INLINE void set_title(const char* format, va_list args) { vsnprintf(this->title, sizeof(this->title), format, args); }
NOTIFY_INLINE void set_content(const char* format, va_list args) { vsnprintf(this->content, sizeof(this->content), format, args); }
public:
NOTIFY_INLINE void set_title(const char* format, ...) { NOTIFY_FORMAT(this->set_title, format); }
NOTIFY_INLINE void set_content(const char* format, ...) { NOTIFY_FORMAT(this->set_content, format); }
NOTIFY_INLINE void set_type(const ImGuiToastType& type) { IM_ASSERT(type < ImGuiToastType_COUNT); this->type = type; };
public:
// Getters
NOTIFY_INLINE char* get_title() { return this->title; };
NOTIFY_INLINE const char* get_default_title()
{
if (!strlen(this->title))
{
switch (this->type)
{
case ImGuiToastType_Success:
return "Success";
case ImGuiToastType_Warning:
return "Warning";
case ImGuiToastType_Error:
return "Error";
case ImGuiToastType_Info:
return "Info";
case ImGuiToastType_None:
default:
return NULL;
}
}
return this->title;
};
NOTIFY_INLINE const ImGuiToastType get_type() { return this->type; };
NOTIFY_INLINE const ImVec4 get_color()
{
switch (this->type)
{
case ImGuiToastType_Success:
return { 0, 255, 0, 255 }; // Green
case ImGuiToastType_Warning:
return { 255, 255, 0, 255 }; // Yellow
case ImGuiToastType_Error:
return { 255, 0, 0, 255 }; // Error
case ImGuiToastType_Info:
return { 0, 157, 255, 255 }; // Blue
case ImGuiToastType_None:
default:
return { 255, 255, 255, 255 }; // White
}
}
NOTIFY_INLINE const char* get_icon()
{
switch (this->type)
{
case ImGuiToastType_Success:
return ICON_FA_CHECK_CIRCLE;
case ImGuiToastType_Warning:
return ICON_FA_EXCLAMATION_TRIANGLE;
case ImGuiToastType_Error:
return ICON_FA_TIMES_CIRCLE;
case ImGuiToastType_Info:
return ICON_FA_INFO_CIRCLE;
case ImGuiToastType_None:
default:
return NULL;
}
}
NOTIFY_INLINE char* get_content() { return this->content; };
NOTIFY_INLINE uint64_t get_elapsed_time() { return GetTickCount64() - this->creation_time; }
NOTIFY_INLINE const ImGuiToastPhase get_phase()
{
const auto elapsed = get_elapsed_time();
if (elapsed > NOTIFY_FADE_IN_OUT_TIME + this->dismiss_time + NOTIFY_FADE_IN_OUT_TIME)
{
return ImGuiToastPhase_Expired;
}
else if (elapsed > NOTIFY_FADE_IN_OUT_TIME + this->dismiss_time)
{
return ImGuiToastPhase_FadeOut;
}
else if (elapsed > NOTIFY_FADE_IN_OUT_TIME)
{
return ImGuiToastPhase_Wait;
}
else
{
return ImGuiToastPhase_FadeIn;
}
}
NOTIFY_INLINE const float get_fade_percent()
{
const auto phase = get_phase();
const auto elapsed = get_elapsed_time();
if (phase == ImGuiToastPhase_FadeIn)
{
return ((float)elapsed / (float)NOTIFY_FADE_IN_OUT_TIME) * NOTIFY_OPACITY;
}
else if (phase == ImGuiToastPhase_FadeOut)
{
return (1.f - (((float)elapsed - (float)NOTIFY_FADE_IN_OUT_TIME - (float)this->dismiss_time) / (float)NOTIFY_FADE_IN_OUT_TIME)) * NOTIFY_OPACITY;
}
return 1.f * NOTIFY_OPACITY;
}
public:
// Constructors
ImGuiToast(ImGuiToastType type, int dismiss_time = NOTIFY_DEFAULT_DISMISS)
{
IM_ASSERT(type < ImGuiToastType_COUNT);
this->type = type;
this->dismiss_time = dismiss_time;
this->creation_time = GetTickCount64();
memset(this->title, 0, sizeof(this->title));
memset(this->content, 0, sizeof(this->content));
}
ImGuiToast(ImGuiToastType type, const char* format, ...) : ImGuiToast(type) { NOTIFY_FORMAT(this->set_content, format); }
ImGuiToast(ImGuiToastType type, int dismiss_time, const char* format, ...) : ImGuiToast(type, dismiss_time) { NOTIFY_FORMAT(this->set_content, format); }
};
namespace ImGui
{
NOTIFY_INLINE std::vector<ImGuiToast> notifications;
/// <summary>
/// Insert a new toast in the list
/// </summary>
NOTIFY_INLINE VOID InsertNotification(const ImGuiToast& toast)
{
notifications.push_back(toast);
}
/// <summary>
/// Remove a toast from the list by its index
/// </summary>
/// <param name="index">index of the toast to remove</param>
NOTIFY_INLINE VOID RemoveNotification(int index)
{
notifications.erase(notifications.begin() + index);
}
/// <summary>
/// Render toasts, call at the end of your rendering!
/// </summary>
NOTIFY_INLINE VOID RenderNotifications()
{
const auto vp_size = GetMainViewport()->Size;
float height = 0.f;
for (auto i = 0; i < notifications.size(); i++)
{
auto* current_toast = &notifications[i];
// Remove toast if expired
if (current_toast->get_phase() == ImGuiToastPhase_Expired)
{
RemoveNotification(i);
continue;
}
// Get icon, title and other data
const auto icon = current_toast->get_icon();
const auto title = current_toast->get_title();
const auto content = current_toast->get_content();
const auto default_title = current_toast->get_default_title();
const auto opacity = current_toast->get_fade_percent(); // Get opacity based of the current phase
// Window rendering
auto text_color = current_toast->get_color();
text_color.w = opacity;
// Generate new unique name for this toast
char window_name[50];
sprintf_s(window_name, "##TOAST%d", i);
//PushStyleColor(ImGuiCol_Text, text_color);
SetNextWindowBgAlpha(opacity);
SetNextWindowPos(ImVec2(vp_size.x - NOTIFY_PADDING_X, vp_size.y - NOTIFY_PADDING_Y - height), ImGuiCond_Always, ImVec2(1.0f, 1.0f));
Begin(window_name, NULL, NOTIFY_TOAST_FLAGS);
// Here we render the toast content
{
PushTextWrapPos(vp_size.x / 3.f); // We want to support multi-line text, this will wrap the text after 1/3 of the screen width
bool was_title_rendered = false;
// If an icon is set
if (!NOTIFY_NULL_OR_EMPTY(icon))
{
//Text(icon); // Render icon text
TextColored(text_color, icon);
was_title_rendered = true;
}
// If a title is set
if (!NOTIFY_NULL_OR_EMPTY(title))
{
// If a title and an icon is set, we want to render on same line
if (!NOTIFY_NULL_OR_EMPTY(icon))
SameLine();
Text(title); // Render title text
was_title_rendered = true;
}
else if (!NOTIFY_NULL_OR_EMPTY(default_title))
{
if (!NOTIFY_NULL_OR_EMPTY(icon))
SameLine();
Text(default_title); // Render default title text (ImGuiToastType_Success -> "Success", etc...)
was_title_rendered = true;
}
// In case ANYTHING was rendered in the top, we want to add a small padding so the text (or icon) looks centered vertically
if (was_title_rendered && !NOTIFY_NULL_OR_EMPTY(content))
{
SetCursorPosY(GetCursorPosY() + 5.f); // Must be a better way to do this!!!!
}
// If a content is set
if (!NOTIFY_NULL_OR_EMPTY(content))
{
if (was_title_rendered)
{
#ifdef NOTIFY_USE_SEPARATOR
Separator();
#endif
}
Text(content); // Render content text
}
PopTextWrapPos();
}
// Save height for next toasts
height += GetWindowHeight() + NOTIFY_PADDING_MESSAGE_Y;
// End
End();
}
}
/// <summary>
/// Adds font-awesome font, must be called ONCE on initialization
/// <param name="FontDataOwnedByAtlas">Fonts are loaded from read-only memory, should be set to false!</param>
/// </summary>
NOTIFY_INLINE VOID MergeIconsWithLatestFont(float font_size, bool FontDataOwnedByAtlas = false)
{
static const ImWchar icons_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
ImFontConfig icons_config;
icons_config.MergeMode = true;
icons_config.PixelSnapH = true;
icons_config.FontDataOwnedByAtlas = FontDataOwnedByAtlas;
GetIO().Fonts->AddFontFromMemoryTTF((void*)fa_solid_900, sizeof(fa_solid_900), font_size, &icons_config, icons_ranges);
}
}
#endif

1
cheat-base/vendor/json vendored Submodule

Submodule cheat-base/vendor/json added at a94430615d

1
cheat-base/vendor/magic_enum vendored Submodule

1
cheat-base/vendor/simpleIni vendored Submodule

1
cheat-base/vendor/stb vendored Submodule

Submodule cheat-base/vendor/stb added at af1a5bc352