mirror of
https://github.com/SunsetMkt/Akebi-GC.git
synced 2025-09-19 20:16:01 +08:00
We rise from the ashes
This commit is contained in:
282
cheat-base/cheat-base.vcxproj
Normal file
282
cheat-base/cheat-base.vcxproj
Normal 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>
|
279
cheat-base/cheat-base.vcxproj.filters
Normal file
279
cheat-base/cheat-base.vcxproj.filters
Normal 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>
|
3
cheat-base/framework/framework.h
Normal file
3
cheat-base/framework/framework.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
5
cheat-base/framework/pch.cpp
Normal file
5
cheat-base/framework/pch.cpp
Normal 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.
|
47
cheat-base/framework/pch.h
Normal file
47
cheat-base/framework/pch.h
Normal 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
|
78
cheat-base/src/cheat-base/HookManager.h
Normal file
78
cheat-base/src/cheat-base/HookManager.h
Normal 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();
|
||||
}
|
||||
};
|
||||
|
||||
|
395
cheat-base/src/cheat-base/Hotkey.cpp
Normal file
395
cheat-base/src/cheat-base/Hotkey.cpp
Normal 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();
|
||||
}
|
43
cheat-base/src/cheat-base/Hotkey.h
Normal file
43
cheat-base/src/cheat-base/Hotkey.h
Normal 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);
|
||||
};
|
143
cheat-base/src/cheat-base/Logger.cpp
Normal file
143
cheat-base/src/cheat-base/Logger.cpp
Normal 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);
|
||||
}
|
48
cheat-base/src/cheat-base/Logger.h
Normal file
48
cheat-base/src/cheat-base/Logger.h
Normal 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;
|
||||
};
|
95
cheat-base/src/cheat-base/Patch.cpp
Normal file
95
cheat-base/src/cheat-base/Patch.cpp
Normal 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;
|
||||
}
|
31
cheat-base/src/cheat-base/Patch.h
Normal file
31
cheat-base/src/cheat-base/Patch.h
Normal 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);
|
||||
|
||||
};
|
||||
|
908
cheat-base/src/cheat-base/PatternScanner.cpp
Normal file
908
cheat-base/src/cheat-base/PatternScanner.cpp
Normal 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;
|
||||
}
|
138
cheat-base/src/cheat-base/PatternScanner.h
Normal file
138
cheat-base/src/cheat-base/PatternScanner.h
Normal 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);
|
||||
};
|
||||
|
87
cheat-base/src/cheat-base/PipeTransfer.cpp
Normal file
87
cheat-base/src/cheat-base/PipeTransfer.cpp
Normal 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);
|
||||
}
|
82
cheat-base/src/cheat-base/PipeTransfer.h
Normal file
82
cheat-base/src/cheat-base/PipeTransfer.h
Normal 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;
|
||||
};
|
||||
|
49
cheat-base/src/cheat-base/ResourceLoader.cpp
Normal file
49
cheat-base/src/cheat-base/ResourceLoader.cpp
Normal 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;
|
||||
}
|
16
cheat-base/src/cheat-base/ResourceLoader.h
Normal file
16
cheat-base/src/cheat-base/ResourceLoader.h
Normal 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;
|
||||
};
|
||||
|
551
cheat-base/src/cheat-base/cheat/CheatManagerBase.cpp
Normal file
551
cheat-base/src/cheat-base/cheat/CheatManagerBase.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
76
cheat-base/src/cheat-base/cheat/CheatManagerBase.h
Normal file
76
cheat-base/src/cheat-base/cheat/CheatManagerBase.h
Normal 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();
|
||||
};
|
||||
}
|
||||
|
||||
|
38
cheat-base/src/cheat-base/cheat/Feature.h
Normal file
38
cheat-base/src/cheat-base/cheat/Feature.h
Normal 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() { };
|
||||
};
|
||||
}
|
||||
|
||||
|
140
cheat-base/src/cheat-base/cheat/misc/Settings.cpp
Normal file
140
cheat-base/src/cheat-base/cheat/misc/Settings.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
44
cheat-base/src/cheat-base/cheat/misc/Settings.h
Normal file
44
cheat-base/src/cheat-base/cheat/misc/Settings.h
Normal 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();
|
||||
};
|
||||
}
|
||||
|
383
cheat-base/src/cheat-base/config/Config.cpp
Normal file
383
cheat-base/src/cheat-base/config/Config.cpp
Normal 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;
|
||||
}
|
||||
}
|
66
cheat-base/src/cheat-base/config/Config.h
Normal file
66
cheat-base/src/cheat-base/config/Config.h
Normal 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;
|
||||
}
|
15
cheat-base/src/cheat-base/config/Field.h
Normal file
15
cheat-base/src/cheat-base/config/Field.h
Normal 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;
|
||||
};
|
||||
}
|
68
cheat-base/src/cheat-base/config/converters.h
Normal file
68
cheat-base/src/cheat-base/config/converters.h
Normal 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
|
||||
|
||||
}
|
100
cheat-base/src/cheat-base/config/fields/Enum.h
Normal file
100
cheat-base/src/cheat-base/config/fields/Enum.h
Normal 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>();
|
||||
}
|
||||
};
|
||||
}
|
83
cheat-base/src/cheat-base/config/fields/Toggle.h
Normal file
83
cheat-base/src/cheat-base/config/fields/Toggle.h
Normal 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
|
||||
}
|
||||
};
|
||||
}
|
105
cheat-base/src/cheat-base/config/internal/FieldBase.h
Normal file
105
cheat-base/src/cheat-base/config/internal/FieldBase.h
Normal 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;
|
||||
};
|
||||
}
|
87
cheat-base/src/cheat-base/config/internal/FieldEntry.h
Normal file
87
cheat-base/src/cheat-base/config/internal/FieldEntry.h
Normal 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;
|
||||
};
|
||||
}
|
42
cheat-base/src/cheat-base/config/internal/FieldSerialize.h
Normal file
42
cheat-base/src/cheat-base/config/internal/FieldSerialize.h
Normal 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;
|
||||
};
|
||||
}
|
211
cheat-base/src/cheat-base/events/event.hpp
Normal file
211
cheat-base/src/cheat-base/events/event.hpp
Normal 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;
|
||||
}
|
||||
};
|
@ -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;
|
||||
};
|
||||
}
|
12
cheat-base/src/cheat-base/events/handlers/eventhandlerptr.h
Normal file
12
cheat-base/src/cheat-base/events/handlers/eventhandlerptr.h
Normal 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...>>;
|
||||
}
|
@ -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 )
|
28
cheat-base/src/cheat-base/events/handlers/handlercast.hpp
Normal file
28
cheat-base/src/cheat-base/events/handlers/handlercast.hpp
Normal 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);
|
||||
}
|
||||
};
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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;
|
||||
};
|
||||
}
|
130
cheat-base/src/cheat-base/events/handlers/methodeventhandler.hpp
Normal file
130
cheat-base/src/cheat-base/events/handlers/methodeventhandler.hpp
Normal 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 )
|
20
cheat-base/src/cheat-base/events/helpers/is_equatable.hpp
Normal file
20
cheat-base/src/cheat-base/events/helpers/is_equatable.hpp
Normal 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;
|
||||
};
|
@ -0,0 +1,9 @@
|
||||
#include "abstracteventjoin.h"
|
||||
|
||||
|
||||
namespace events::joins
|
||||
{
|
||||
AbstractEventJoin::AbstractEventJoin() {}
|
||||
|
||||
AbstractEventJoin::~AbstractEventJoin() {}
|
||||
}
|
20
cheat-base/src/cheat-base/events/joins/abstracteventjoin.h
Normal file
20
cheat-base/src/cheat-base/events/joins/abstracteventjoin.h
Normal 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();
|
||||
};
|
||||
}
|
62
cheat-base/src/cheat-base/events/joins/eventjoinwrapper.cpp
Normal file
62
cheat-base/src/cheat-base/events/joins/eventjoinwrapper.cpp
Normal 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
|
44
cheat-base/src/cheat-base/events/joins/eventjoinwrapper.h
Normal file
44
cheat-base/src/cheat-base/events/joins/eventjoinwrapper.h
Normal 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;
|
||||
}
|
14
cheat-base/src/cheat-base/events/joins/eventjoinwrapper.hpp
Normal file
14
cheat-base/src/cheat-base/events/joins/eventjoinwrapper.hpp
Normal 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 ) ) )
|
||||
{ }
|
||||
}
|
31
cheat-base/src/cheat-base/events/joins/handlereventjoin.h
Normal file
31
cheat-base/src/cheat-base/events/joins/handlereventjoin.h
Normal 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;
|
||||
};
|
||||
}
|
26
cheat-base/src/cheat-base/events/joins/handlereventjoin.hpp
Normal file
26
cheat-base/src/cheat-base/events/joins/handlereventjoin.hpp
Normal 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);
|
||||
}
|
||||
}
|
10
cheat-base/src/cheat-base/globals.cpp
Normal file
10
cheat-base/src/cheat-base/globals.cpp
Normal file
@ -0,0 +1,10 @@
|
||||
#include <pch.h>
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
namespace events
|
||||
{
|
||||
TCancelableEvent<short> KeyUpEvent{};
|
||||
TCancelableEvent<HWND, UINT, WPARAM, LPARAM> WndProcEvent {};
|
||||
TEvent<> RenderEvent {};
|
||||
}
|
12
cheat-base/src/cheat-base/globals.h
Normal file
12
cheat-base/src/cheat-base/globals.h
Normal 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;
|
||||
}
|
54
cheat-base/src/cheat-base/render/ImageLoader.cpp
Normal file
54
cheat-base/src/cheat-base/render/ImageLoader.cpp
Normal 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;
|
||||
}
|
24
cheat-base/src/cheat-base/render/ImageLoader.h
Normal file
24
cheat-base/src/cheat-base/render/ImageLoader.h
Normal 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 {};
|
||||
};
|
141
cheat-base/src/cheat-base/render/backend/dx11-hook.cpp
Normal file
141
cheat-base/src/cheat-base/render/backend/dx11-hook.cpp
Normal 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;
|
||||
}
|
22
cheat-base/src/cheat-base/render/backend/dx11-hook.h
Normal file
22
cheat-base/src/cheat-base/render/backend/dx11-hook.h
Normal 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{};
|
||||
};
|
||||
}
|
740
cheat-base/src/cheat-base/render/gui-util.cpp
Normal file
740
cheat-base/src/cheat-base/render/gui-util.cpp
Normal 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;
|
||||
}
|
163
cheat-base/src/cheat-base/render/gui-util.h
Normal file
163
cheat-base/src/cheat-base/render/gui-util.h
Normal 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);
|
||||
}
|
291
cheat-base/src/cheat-base/render/renderer.cpp
Normal file
291
cheat-base/src/cheat-base/render/renderer.cpp
Normal 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);
|
||||
}
|
||||
}
|
22
cheat-base/src/cheat-base/render/renderer.h
Normal file
22
cheat-base/src/cheat-base/render/renderer.h
Normal 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();
|
||||
}
|
87
cheat-base/src/cheat-base/thread-safe.h
Normal file
87
cheat-base/src/cheat-base/thread-safe.h
Normal 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_;
|
||||
};
|
264
cheat-base/src/cheat-base/util.cpp
Normal file
264
cheat-base/src/cheat-base/util.cpp
Normal 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;
|
||||
}
|
||||
}
|
79
cheat-base/src/cheat-base/util.h
Normal file
79
cheat-base/src/cheat-base/util.h
Normal 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;
|
||||
}
|
||||
}
|
BIN
cheat-base/vendor/detours/detours-x64.lib
vendored
Normal file
BIN
cheat-base/vendor/detours/detours-x64.lib
vendored
Normal file
Binary file not shown.
1227
cheat-base/vendor/detours/detours.h
vendored
Normal file
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
27
cheat-base/vendor/detours/detver.h
vendored
Normal 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
1
cheat-base/vendor/fmt
vendored
Submodule
Submodule cheat-base/vendor/fmt added at 86e27ccb41
1
cheat-base/vendor/imgui
vendored
Submodule
1
cheat-base/vendor/imgui
vendored
Submodule
Submodule cheat-base/vendor/imgui added at af916cdf1a
8115
cheat-base/vendor/imgui-notify-v2/fa_solid_900.h
vendored
Normal file
8115
cheat-base/vendor/imgui-notify-v2/fa_solid_900.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1821
cheat-base/vendor/imgui-notify-v2/font_awesome_5.h
vendored
Normal file
1821
cheat-base/vendor/imgui-notify-v2/font_awesome_5.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
357
cheat-base/vendor/imgui-notify-v2/imgui_notify.h
vendored
Normal file
357
cheat-base/vendor/imgui-notify-v2/imgui_notify.h
vendored
Normal 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 = ¬ifications[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
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/magic_enum
vendored
Submodule
Submodule cheat-base/vendor/magic_enum added at d1ccd22c85
1
cheat-base/vendor/simpleIni
vendored
Submodule
1
cheat-base/vendor/simpleIni
vendored
Submodule
Submodule cheat-base/vendor/simpleIni added at 9b3ed7ec81
1
cheat-base/vendor/stb
vendored
Submodule
1
cheat-base/vendor/stb
vendored
Submodule
Submodule cheat-base/vendor/stb added at af1a5bc352
Reference in New Issue
Block a user