diff --git a/apex.sln b/apex.sln deleted file mode 100644 index 6f04a266..00000000 --- a/apex.sln +++ /dev/null @@ -1,71 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30621.155 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r5launcher", "r5launcher\r5launcher.vcxproj", "{18F8C75E-3844-4AA6-AB93-980A08253519}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r5dev", "r5dev\r5dev.vcxproj", "{28CC6B4F-7A95-4933-ADA9-65E38D48516D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r5dedicated", "r5dedicated\r5dedicated.vcxproj", "{71988D92-343C-49AB-B52B-0AE0E83B0401}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Minhook", "external\minhook\libMinHook.vcxproj", "{F142A341-5EE0-442D-A15F-98AE9B48DBAE}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r5net", "r5net\r5net.vcxproj", "{F04BE619-0326-4FF1-B06B-FBE882E04D5E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x64.ActiveCfg = Debug|x64 - {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x64.Build.0 = Debug|x64 - {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x86.ActiveCfg = Debug|Win32 - {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x86.Build.0 = Debug|Win32 - {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x64.ActiveCfg = Release|x64 - {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x64.Build.0 = Release|x64 - {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x86.ActiveCfg = Release|Win32 - {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x86.Build.0 = Release|Win32 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x64.ActiveCfg = Debug|x64 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x64.Build.0 = Debug|x64 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x86.ActiveCfg = Debug|Win32 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x86.Build.0 = Debug|Win32 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x64.ActiveCfg = Release|x64 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x64.Build.0 = Release|x64 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x86.ActiveCfg = Release|Win32 - {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x86.Build.0 = Release|Win32 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Debug|x64.ActiveCfg = Debug|x64 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Debug|x64.Build.0 = Debug|x64 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Debug|x86.ActiveCfg = Debug|Win32 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Debug|x86.Build.0 = Debug|Win32 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Release|x64.ActiveCfg = Release|x64 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Release|x64.Build.0 = Release|x64 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Release|x86.ActiveCfg = Release|Win32 - {71988D92-343C-49AB-B52B-0AE0E83B0401}.Release|x86.Build.0 = Release|Win32 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.ActiveCfg = Debug|x64 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x64.Build.0 = Debug|x64 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x86.ActiveCfg = Debug|Win32 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Debug|x86.Build.0 = Debug|Win32 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.ActiveCfg = Release|x64 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x64.Build.0 = Release|x64 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x86.ActiveCfg = Release|Win32 - {F142A341-5EE0-442D-A15F-98AE9B48DBAE}.Release|x86.Build.0 = Release|Win32 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Debug|x64.ActiveCfg = Debug|x64 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Debug|x64.Build.0 = Debug|x64 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Debug|x86.ActiveCfg = Debug|Win32 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Debug|x86.Build.0 = Debug|Win32 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Release|x64.ActiveCfg = Release|x64 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Release|x64.Build.0 = Release|x64 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Release|x86.ActiveCfg = Release|Win32 - {F04BE619-0326-4FF1-B06B-FBE882E04D5E}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {5E5FE02E-6BCE-4BAF-9948-C56476039C3C} - EndGlobalSection -EndGlobal diff --git a/detours.sln b/detours.sln new file mode 100644 index 00000000..4aed7e75 --- /dev/null +++ b/detours.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31808.319 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r5devdll", "r5dev\r5dev.vcxproj", "{28CC6B4F-7A95-4933-ADA9-65E38D48516D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdklauncher", "r5dev\sdklauncher.vcxproj", "{18F8C75E-3844-4AA6-AB93-980A08253519}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dedicated", "r5dev\dedicated.vcxproj", "{ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x64.ActiveCfg = Debug|x64 + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x64.Build.0 = Debug|x64 + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x86.ActiveCfg = Debug|Win32 + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Debug|x86.Build.0 = Debug|Win32 + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x64.ActiveCfg = Release|x64 + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x64.Build.0 = Release|x64 + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x86.ActiveCfg = Release|Win32 + {28CC6B4F-7A95-4933-ADA9-65E38D48516D}.Release|x86.Build.0 = Release|Win32 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x64.ActiveCfg = Debug|x64 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x64.Build.0 = Debug|x64 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x86.ActiveCfg = Debug|Win32 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Debug|x86.Build.0 = Debug|Win32 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x64.ActiveCfg = Release|x64 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x64.Build.0 = Release|x64 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x86.ActiveCfg = Release|Win32 + {18F8C75E-3844-4AA6-AB93-980A08253519}.Release|x86.Build.0 = Release|Win32 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Debug|x64.ActiveCfg = Debug|x64 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Debug|x64.Build.0 = Debug|x64 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Debug|x86.ActiveCfg = Debug|Win32 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Debug|x86.Build.0 = Debug|Win32 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Release|x64.ActiveCfg = Release|x64 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Release|x64.Build.0 = Release|x64 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Release|x86.ActiveCfg = Release|Win32 + {ED2C50B3-7C2C-4E44-988E-DAA059F72B9C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5E5FE02E-6BCE-4BAF-9948-C56476039C3C} + EndGlobalSection +EndGlobal diff --git a/external/detours/libs/detours.lib b/external/detours/libs/detours.lib deleted file mode 100644 index a813633c..00000000 Binary files a/external/detours/libs/detours.lib and /dev/null differ diff --git a/external/detours/libs/detours.pdb b/external/detours/libs/detours.pdb deleted file mode 100644 index 7e39e0c0..00000000 Binary files a/external/detours/libs/detours.pdb and /dev/null differ diff --git a/external/detours/libs/detours_trace.lib b/external/detours/libs/detours_trace.lib deleted file mode 100644 index 1bda1142..00000000 Binary files a/external/detours/libs/detours_trace.lib and /dev/null differ diff --git a/external/detours/libs/syelog.lib b/external/detours/libs/syelog.lib deleted file mode 100644 index 6012eef0..00000000 Binary files a/external/detours/libs/syelog.lib and /dev/null differ diff --git a/external/minhook/include/MinHook.h b/external/minhook/include/MinHook.h deleted file mode 100644 index 492d83f8..00000000 --- a/external/minhook/include/MinHook.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__) - #error MinHook supports only x86 and x64 systems. -#endif - -#include - -// MinHook Error Codes. -typedef enum MH_STATUS -{ - // Unknown error. Should not be returned. - MH_UNKNOWN = -1, - - // Successful. - MH_OK = 0, - - // MinHook is already initialized. - MH_ERROR_ALREADY_INITIALIZED, - - // MinHook is not initialized yet, or already uninitialized. - MH_ERROR_NOT_INITIALIZED, - - // The hook for the specified target function is already created. - MH_ERROR_ALREADY_CREATED, - - // The hook for the specified target function is not created yet. - MH_ERROR_NOT_CREATED, - - // The hook for the specified target function is already enabled. - MH_ERROR_ENABLED, - - // The hook for the specified target function is not enabled yet, or already - // disabled. - MH_ERROR_DISABLED, - - // The specified pointer is invalid. It points the address of non-allocated - // and/or non-executable region. - MH_ERROR_NOT_EXECUTABLE, - - // The specified target function cannot be hooked. - MH_ERROR_UNSUPPORTED_FUNCTION, - - // Failed to allocate memory. - MH_ERROR_MEMORY_ALLOC, - - // Failed to change the memory protection. - MH_ERROR_MEMORY_PROTECT, - - // The specified module is not loaded. - MH_ERROR_MODULE_NOT_FOUND, - - // The specified function is not found. - MH_ERROR_FUNCTION_NOT_FOUND -} -MH_STATUS; - -// Can be passed as a parameter to MH_EnableHook, MH_DisableHook, -// MH_QueueEnableHook or MH_QueueDisableHook. -#define MH_ALL_HOOKS NULL - -#ifdef __cplusplus -extern "C" { -#endif - - // Initialize the MinHook library. You must call this function EXACTLY ONCE - // at the beginning of your program. - MH_STATUS WINAPI MH_Initialize(VOID); - - // Uninitialize the MinHook library. You must call this function EXACTLY - // ONCE at the end of your program. - MH_STATUS WINAPI MH_Uninitialize(VOID); - - // Creates a hook for the specified target function, in disabled state. - // Parameters: - // pTarget [in] A pointer to the target function, which will be - // overridden by the detour function. - // pDetour [in] A pointer to the detour function, which will override - // the target function. - // ppOriginal [out] A pointer to the trampoline function, which will be - // used to call the original target function. - // This parameter can be NULL. - MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal); - - // Creates a hook for the specified API function, in disabled state. - // Parameters: - // pszModule [in] A pointer to the loaded module name which contains the - // target function. - // pszProcName [in] A pointer to the target function name, which will be - // overridden by the detour function. - // pDetour [in] A pointer to the detour function, which will override - // the target function. - // ppOriginal [out] A pointer to the trampoline function, which will be - // used to call the original target function. - // This parameter can be NULL. - MH_STATUS WINAPI MH_CreateHookApi( - LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal); - - // Creates a hook for the specified API function, in disabled state. - // Parameters: - // pszModule [in] A pointer to the loaded module name which contains the - // target function. - // pszProcName [in] A pointer to the target function name, which will be - // overridden by the detour function. - // pDetour [in] A pointer to the detour function, which will override - // the target function. - // ppOriginal [out] A pointer to the trampoline function, which will be - // used to call the original target function. - // This parameter can be NULL. - // ppTarget [out] A pointer to the target function, which will be used - // with other functions. - // This parameter can be NULL. - MH_STATUS WINAPI MH_CreateHookApiEx( - LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget); - - // Removes an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget); - - // Enables an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // enabled in one go. - MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget); - - // Disables an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // disabled in one go. - MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget); - - // Queues to enable an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // queued to be enabled. - MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget); - - // Queues to disable an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // queued to be disabled. - MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget); - - // Applies all queued changes in one go. - MH_STATUS WINAPI MH_ApplyQueued(VOID); - - // Translates the MH_STATUS to its name as a string. - const char * WINAPI MH_StatusToString(MH_STATUS status); - -#ifdef __cplusplus -} -#endif diff --git a/external/minhook/libMinHook.vcxproj b/external/minhook/libMinHook.vcxproj deleted file mode 100644 index e4bfd790..00000000 --- a/external/minhook/libMinHook.vcxproj +++ /dev/null @@ -1,173 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - - - - - - - - - - - - - - - - - - {F142A341-5EE0-442D-A15F-98AE9B48DBAE} - libMinHook - Win32Proj - 10.0 - Minhook - - - - StaticLibrary - Unicode - true - v142 - - - StaticLibrary - Unicode - v142 - - - StaticLibrary - Unicode - true - v142 - - - StaticLibrary - Unicode - v142 - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.40219.1 - $(SolutionDir)lib\$(Configuration)\ - $(Platform)\$(Configuration)\$(ProjectName)\ - $(ProjectDir)lib\$(Configuration) - $(SolutionDir)build\$(ProjectName)\$(Configuration) - $(SolutionDir)lib\$(Configuration)\ - $(Platform)\$(Configuration)\$(ProjectName)\ - $(ProjectDir)lib\$(Configuration) - $(SolutionDir)build\$(ProjectName)\$(Configuration) - $(ProjectName).x86 - $(ProjectName).x86 - $(ProjectName).x64 - $(ProjectName).x64 - - - - Disabled - %(AdditionalIncludeDirectories) - WIN32;_DEBUG;_LIB;STRICT;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebug - Level3 - None - NoExtensions - - - - - - X64 - - - Disabled - %(AdditionalIncludeDirectories) - WIN32;_DEBUG;_LIB;STRICT;%(PreprocessorDefinitions) - false - EnableFastChecks - MultiThreadedDebug - Level3 - None - NotSet - stdcpp17 - Default - - - - - - MinSpace - true - %(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;STRICT;%(PreprocessorDefinitions) - false - MultiThreaded - true - Level3 - None - AnySuitable - true - NoExtensions - - - - - - X64 - - - MinSpace - true - %(AdditionalIncludeDirectories) - WIN32;NDEBUG;_LIB;STRICT;%(PreprocessorDefinitions) - false - MultiThreaded - true - Level3 - None - true - AnySuitable - stdcpp17 - Default - - - - - - - \ No newline at end of file diff --git a/external/minhook/libMinHook.vcxproj.filters b/external/minhook/libMinHook.vcxproj.filters deleted file mode 100644 index 76694c63..00000000 --- a/external/minhook/libMinHook.vcxproj.filters +++ /dev/null @@ -1,57 +0,0 @@ - - - - - src - - - src - - - src - - - src\hde - - - src\hde - - - - - src - - - src - - - src\hde - - - src\hde - - - src\hde - - - src\hde - - - src\hde - - - include - - - - - {62eb648f-1c53-4909-8805-77a8cdc6f408} - - - {161a8d4e-b988-418b-a31e-d089ae0e9e22} - - - {dd59bf86-2fa2-4576-bb49-7b65617b67a5} - - - \ No newline at end of file diff --git a/external/minhook/src/buffer.c b/external/minhook/src/buffer.c deleted file mode 100644 index 55412b0f..00000000 --- a/external/minhook/src/buffer.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include "buffer.h" - -// Size of each memory block. (= page size of VirtualAlloc) -#define MEMORY_BLOCK_SIZE 0x1000 - -// Max range for seeking a memory block. (= 1024MB) -#define MAX_MEMORY_RANGE 0x40000000 - -// Memory protection flags to check the executable address. -#define PAGE_EXECUTE_FLAGS \ - (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) - -// Memory slot. -typedef struct _MEMORY_SLOT -{ - union - { - struct _MEMORY_SLOT *pNext; - UINT8 buffer[MEMORY_SLOT_SIZE]; - }; -} MEMORY_SLOT, *PMEMORY_SLOT; - -// Memory block info. Placed at the head of each block. -typedef struct _MEMORY_BLOCK -{ - struct _MEMORY_BLOCK *pNext; - PMEMORY_SLOT pFree; // First element of the free slot list. - UINT usedCount; -} MEMORY_BLOCK, *PMEMORY_BLOCK; - -//------------------------------------------------------------------------- -// Global Variables: -//------------------------------------------------------------------------- - -// First element of the memory block list. -PMEMORY_BLOCK g_pMemoryBlocks; - -//------------------------------------------------------------------------- -VOID InitializeBuffer(VOID) -{ - // Nothing to do for now. -} - -//------------------------------------------------------------------------- -VOID UninitializeBuffer(VOID) -{ - PMEMORY_BLOCK pBlock = g_pMemoryBlocks; - g_pMemoryBlocks = NULL; - - while (pBlock) - { - PMEMORY_BLOCK pNext = pBlock->pNext; - VirtualFree(pBlock, 0, MEM_RELEASE); - pBlock = pNext; - } -} - -//------------------------------------------------------------------------- -#if defined(_M_X64) || defined(__x86_64__) -static LPVOID FindPrevFreeRegion(LPVOID pAddress, LPVOID pMinAddr, DWORD dwAllocationGranularity) -{ - ULONG_PTR tryAddr = (ULONG_PTR)pAddress; - - // Round down to the allocation granularity. - tryAddr -= tryAddr % dwAllocationGranularity; - - // Start from the previous allocation granularity multiply. - tryAddr -= dwAllocationGranularity; - - while (tryAddr >= (ULONG_PTR)pMinAddr) - { - MEMORY_BASIC_INFORMATION mbi; - if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0) - break; - - if (mbi.State == MEM_FREE) - return (LPVOID)tryAddr; - - if ((ULONG_PTR)mbi.AllocationBase < dwAllocationGranularity) - break; - - tryAddr = (ULONG_PTR)mbi.AllocationBase - dwAllocationGranularity; - } - - return NULL; -} -#endif - -//------------------------------------------------------------------------- -#if defined(_M_X64) || defined(__x86_64__) -static LPVOID FindNextFreeRegion(LPVOID pAddress, LPVOID pMaxAddr, DWORD dwAllocationGranularity) -{ - ULONG_PTR tryAddr = (ULONG_PTR)pAddress; - - // Round down to the allocation granularity. - tryAddr -= tryAddr % dwAllocationGranularity; - - // Start from the next allocation granularity multiply. - tryAddr += dwAllocationGranularity; - - while (tryAddr <= (ULONG_PTR)pMaxAddr) - { - MEMORY_BASIC_INFORMATION mbi; - if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0) - break; - - if (mbi.State == MEM_FREE) - return (LPVOID)tryAddr; - - tryAddr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize; - - // Round up to the next allocation granularity. - tryAddr += dwAllocationGranularity - 1; - tryAddr -= tryAddr % dwAllocationGranularity; - } - - return NULL; -} -#endif - -//------------------------------------------------------------------------- -static PMEMORY_BLOCK GetMemoryBlock(LPVOID pOrigin) -{ - PMEMORY_BLOCK pBlock; -#if defined(_M_X64) || defined(__x86_64__) - ULONG_PTR minAddr; - ULONG_PTR maxAddr; - - SYSTEM_INFO si; - GetSystemInfo(&si); - minAddr = (ULONG_PTR)si.lpMinimumApplicationAddress; - maxAddr = (ULONG_PTR)si.lpMaximumApplicationAddress; - - // pOrigin ± 512MB - if ((ULONG_PTR)pOrigin > MAX_MEMORY_RANGE && minAddr < (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE) - minAddr = (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE; - - if (maxAddr > (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE) - maxAddr = (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE; - - // Make room for MEMORY_BLOCK_SIZE bytes. - maxAddr -= MEMORY_BLOCK_SIZE - 1; -#endif - - // Look the registered blocks for a reachable one. - for (pBlock = g_pMemoryBlocks; pBlock != NULL; pBlock = pBlock->pNext) - { -#if defined(_M_X64) || defined(__x86_64__) - // Ignore the blocks too far. - if ((ULONG_PTR)pBlock < minAddr || (ULONG_PTR)pBlock >= maxAddr) - continue; -#endif - // The block has at least one unused slot. - if (pBlock->pFree != NULL) - return pBlock; - } - -#if defined(_M_X64) || defined(__x86_64__) - // Alloc a new block above if not found. - { - LPVOID pAlloc = pOrigin; - while ((ULONG_PTR)pAlloc >= minAddr) - { - pAlloc = FindPrevFreeRegion(pAlloc, (LPVOID)minAddr, si.dwAllocationGranularity); - if (pAlloc == NULL) - break; - - pBlock = (PMEMORY_BLOCK)VirtualAlloc( - pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (pBlock != NULL) - break; - } - } - - // Alloc a new block below if not found. - if (pBlock == NULL) - { - LPVOID pAlloc = pOrigin; - while ((ULONG_PTR)pAlloc <= maxAddr) - { - pAlloc = FindNextFreeRegion(pAlloc, (LPVOID)maxAddr, si.dwAllocationGranularity); - if (pAlloc == NULL) - break; - - pBlock = (PMEMORY_BLOCK)VirtualAlloc( - pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (pBlock != NULL) - break; - } - } -#else - // In x86 mode, a memory block can be placed anywhere. - pBlock = (PMEMORY_BLOCK)VirtualAlloc( - NULL, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); -#endif - - if (pBlock != NULL) - { - // Build a linked list of all the slots. - PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBlock + 1; - pBlock->pFree = NULL; - pBlock->usedCount = 0; - do - { - pSlot->pNext = pBlock->pFree; - pBlock->pFree = pSlot; - pSlot++; - } while ((ULONG_PTR)pSlot - (ULONG_PTR)pBlock <= MEMORY_BLOCK_SIZE - MEMORY_SLOT_SIZE); - - pBlock->pNext = g_pMemoryBlocks; - g_pMemoryBlocks = pBlock; - } - - return pBlock; -} - -//------------------------------------------------------------------------- -LPVOID AllocateBuffer(LPVOID pOrigin) -{ - PMEMORY_SLOT pSlot; - PMEMORY_BLOCK pBlock = GetMemoryBlock(pOrigin); - if (pBlock == NULL) - return NULL; - - // Remove an unused slot from the list. - pSlot = pBlock->pFree; - pBlock->pFree = pSlot->pNext; - pBlock->usedCount++; -#ifdef _DEBUG - // Fill the slot with INT3 for debugging. - memset(pSlot, 0xCC, sizeof(MEMORY_SLOT)); -#endif - return pSlot; -} - -//------------------------------------------------------------------------- -VOID FreeBuffer(LPVOID pBuffer) -{ - PMEMORY_BLOCK pBlock = g_pMemoryBlocks; - PMEMORY_BLOCK pPrev = NULL; - ULONG_PTR pTargetBlock = ((ULONG_PTR)pBuffer / MEMORY_BLOCK_SIZE) * MEMORY_BLOCK_SIZE; - - while (pBlock != NULL) - { - if ((ULONG_PTR)pBlock == pTargetBlock) - { - PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBuffer; -#ifdef _DEBUG - // Clear the released slot for debugging. - memset(pSlot, 0x00, sizeof(MEMORY_SLOT)); -#endif - // Restore the released slot to the list. - pSlot->pNext = pBlock->pFree; - pBlock->pFree = pSlot; - pBlock->usedCount--; - - // Free if unused. - if (pBlock->usedCount == 0) - { - if (pPrev) - pPrev->pNext = pBlock->pNext; - else - g_pMemoryBlocks = pBlock->pNext; - - VirtualFree(pBlock, 0, MEM_RELEASE); - } - - break; - } - - pPrev = pBlock; - pBlock = pBlock->pNext; - } -} - -//------------------------------------------------------------------------- -BOOL IsExecutableAddress(LPVOID pAddress) -{ - MEMORY_BASIC_INFORMATION mi; - VirtualQuery(pAddress, &mi, sizeof(mi)); - - return (mi.State == MEM_COMMIT && (mi.Protect & PAGE_EXECUTE_FLAGS)); -} diff --git a/external/minhook/src/buffer.h b/external/minhook/src/buffer.h deleted file mode 100644 index 204d551b..00000000 --- a/external/minhook/src/buffer.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -// Size of each memory slot. -#if defined(_M_X64) || defined(__x86_64__) - #define MEMORY_SLOT_SIZE 64 -#else - #define MEMORY_SLOT_SIZE 32 -#endif - -VOID InitializeBuffer(VOID); -VOID UninitializeBuffer(VOID); -LPVOID AllocateBuffer(LPVOID pOrigin); -VOID FreeBuffer(LPVOID pBuffer); -BOOL IsExecutableAddress(LPVOID pAddress); diff --git a/external/minhook/src/hde/hde32.c b/external/minhook/src/hde/hde32.c deleted file mode 100644 index eb6af9b8..00000000 --- a/external/minhook/src/hde/hde32.c +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Hacker Disassembler Engine 32 C - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - */ - -#if defined(_M_IX86) || defined(__i386__) - -#include -#include "hde32.h" -#include "table32.h" - -unsigned int hde32_disasm(const void *code, hde32s *hs) -{ - uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; - uint8_t *ht = hde32_table, m_mod, m_reg, m_rm, disp_size = 0; - - memset(hs, 0, sizeof(hde32s)); - - for (x = 16; x; x--) - switch (c = *p++) { - case 0xf3: - hs->p_rep = c; - pref |= PRE_F3; - break; - case 0xf2: - hs->p_rep = c; - pref |= PRE_F2; - break; - case 0xf0: - hs->p_lock = c; - pref |= PRE_LOCK; - break; - case 0x26: case 0x2e: case 0x36: - case 0x3e: case 0x64: case 0x65: - hs->p_seg = c; - pref |= PRE_SEG; - break; - case 0x66: - hs->p_66 = c; - pref |= PRE_66; - break; - case 0x67: - hs->p_67 = c; - pref |= PRE_67; - break; - default: - goto pref_done; - } - pref_done: - - hs->flags = (uint32_t)pref << 23; - - if (!pref) - pref |= PRE_NONE; - - if ((hs->opcode = c) == 0x0f) { - hs->opcode2 = c = *p++; - ht += DELTA_OPCODES; - } else if (c >= 0xa0 && c <= 0xa3) { - if (pref & PRE_67) - pref |= PRE_66; - else - pref &= ~PRE_66; - } - - opcode = c; - cflags = ht[ht[opcode / 4] + (opcode % 4)]; - - if (cflags == C_ERROR) { - hs->flags |= F_ERROR | F_ERROR_OPCODE; - cflags = 0; - if ((opcode & -3) == 0x24) - cflags++; - } - - x = 0; - if (cflags & C_GROUP) { - uint16_t t; - t = *(uint16_t *)(ht + (cflags & 0x7f)); - cflags = (uint8_t)t; - x = (uint8_t)(t >> 8); - } - - if (hs->opcode2) { - ht = hde32_table + DELTA_PREFIXES; - if (ht[ht[opcode / 4] + (opcode % 4)] & pref) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - } - - if (cflags & C_MODRM) { - hs->flags |= F_MODRM; - hs->modrm = c = *p++; - hs->modrm_mod = m_mod = c >> 6; - hs->modrm_rm = m_rm = c & 7; - hs->modrm_reg = m_reg = (c & 0x3f) >> 3; - - if (x && ((x << m_reg) & 0x80)) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - - if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { - uint8_t t = opcode - 0xd9; - if (m_mod == 3) { - ht = hde32_table + DELTA_FPU_MODRM + t*8; - t = ht[m_reg] << m_rm; - } else { - ht = hde32_table + DELTA_FPU_REG; - t = ht[t] << m_reg; - } - if (t & 0x80) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - } - - if (pref & PRE_LOCK) { - if (m_mod == 3) { - hs->flags |= F_ERROR | F_ERROR_LOCK; - } else { - uint8_t *table_end, op = opcode; - if (hs->opcode2) { - ht = hde32_table + DELTA_OP2_LOCK_OK; - table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; - } else { - ht = hde32_table + DELTA_OP_LOCK_OK; - table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; - op &= -2; - } - for (; ht != table_end; ht++) - if (*ht++ == op) { - if (!((*ht << m_reg) & 0x80)) - goto no_lock_error; - else - break; - } - hs->flags |= F_ERROR | F_ERROR_LOCK; - no_lock_error: - ; - } - } - - if (hs->opcode2) { - switch (opcode) { - case 0x20: case 0x22: - m_mod = 3; - if (m_reg > 4 || m_reg == 1) - goto error_operand; - else - goto no_error_operand; - case 0x21: case 0x23: - m_mod = 3; - if (m_reg == 4 || m_reg == 5) - goto error_operand; - else - goto no_error_operand; - } - } else { - switch (opcode) { - case 0x8c: - if (m_reg > 5) - goto error_operand; - else - goto no_error_operand; - case 0x8e: - if (m_reg == 1 || m_reg > 5) - goto error_operand; - else - goto no_error_operand; - } - } - - if (m_mod == 3) { - uint8_t *table_end; - if (hs->opcode2) { - ht = hde32_table + DELTA_OP2_ONLY_MEM; - table_end = ht + sizeof(hde32_table) - DELTA_OP2_ONLY_MEM; - } else { - ht = hde32_table + DELTA_OP_ONLY_MEM; - table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; - } - for (; ht != table_end; ht += 2) - if (*ht++ == opcode) { - if ((*ht++ & pref) && !((*ht << m_reg) & 0x80)) - goto error_operand; - else - break; - } - goto no_error_operand; - } else if (hs->opcode2) { - switch (opcode) { - case 0x50: case 0xd7: case 0xf7: - if (pref & (PRE_NONE | PRE_66)) - goto error_operand; - break; - case 0xd6: - if (pref & (PRE_F2 | PRE_F3)) - goto error_operand; - break; - case 0xc5: - goto error_operand; - } - goto no_error_operand; - } else - goto no_error_operand; - - error_operand: - hs->flags |= F_ERROR | F_ERROR_OPERAND; - no_error_operand: - - c = *p++; - if (m_reg <= 1) { - if (opcode == 0xf6) - cflags |= C_IMM8; - else if (opcode == 0xf7) - cflags |= C_IMM_P66; - } - - switch (m_mod) { - case 0: - if (pref & PRE_67) { - if (m_rm == 6) - disp_size = 2; - } else - if (m_rm == 5) - disp_size = 4; - break; - case 1: - disp_size = 1; - break; - case 2: - disp_size = 2; - if (!(pref & PRE_67)) - disp_size <<= 1; - break; - } - - if (m_mod != 3 && m_rm == 4 && !(pref & PRE_67)) { - hs->flags |= F_SIB; - p++; - hs->sib = c; - hs->sib_scale = c >> 6; - hs->sib_index = (c & 0x3f) >> 3; - if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) - disp_size = 4; - } - - p--; - switch (disp_size) { - case 1: - hs->flags |= F_DISP8; - hs->disp.disp8 = *p; - break; - case 2: - hs->flags |= F_DISP16; - hs->disp.disp16 = *(uint16_t *)p; - break; - case 4: - hs->flags |= F_DISP32; - hs->disp.disp32 = *(uint32_t *)p; - break; - } - p += disp_size; - } else if (pref & PRE_LOCK) - hs->flags |= F_ERROR | F_ERROR_LOCK; - - if (cflags & C_IMM_P66) { - if (cflags & C_REL32) { - if (pref & PRE_66) { - hs->flags |= F_IMM16 | F_RELATIVE; - hs->imm.imm16 = *(uint16_t *)p; - p += 2; - goto disasm_done; - } - goto rel32_ok; - } - if (pref & PRE_66) { - hs->flags |= F_IMM16; - hs->imm.imm16 = *(uint16_t *)p; - p += 2; - } else { - hs->flags |= F_IMM32; - hs->imm.imm32 = *(uint32_t *)p; - p += 4; - } - } - - if (cflags & C_IMM16) { - if (hs->flags & F_IMM32) { - hs->flags |= F_IMM16; - hs->disp.disp16 = *(uint16_t *)p; - } else if (hs->flags & F_IMM16) { - hs->flags |= F_2IMM16; - hs->disp.disp16 = *(uint16_t *)p; - } else { - hs->flags |= F_IMM16; - hs->imm.imm16 = *(uint16_t *)p; - } - p += 2; - } - if (cflags & C_IMM8) { - hs->flags |= F_IMM8; - hs->imm.imm8 = *p++; - } - - if (cflags & C_REL32) { - rel32_ok: - hs->flags |= F_IMM32 | F_RELATIVE; - hs->imm.imm32 = *(uint32_t *)p; - p += 4; - } else if (cflags & C_REL8) { - hs->flags |= F_IMM8 | F_RELATIVE; - hs->imm.imm8 = *p++; - } - - disasm_done: - - if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { - hs->flags |= F_ERROR | F_ERROR_LENGTH; - hs->len = 15; - } - - return (unsigned int)hs->len; -} - -#endif // defined(_M_IX86) || defined(__i386__) diff --git a/external/minhook/src/hde/hde32.h b/external/minhook/src/hde/hde32.h deleted file mode 100644 index 1112450d..00000000 --- a/external/minhook/src/hde/hde32.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Hacker Disassembler Engine 32 - * Copyright (c) 2006-2009, Vyacheslav Patkov. - * All rights reserved. - * - * hde32.h: C/C++ header file - * - */ - -#ifndef _HDE32_H_ -#define _HDE32_H_ - -/* stdint.h - C99 standard header - * http://en.wikipedia.org/wiki/stdint.h - * - * if your compiler doesn't contain "stdint.h" header (for - * example, Microsoft Visual C++), you can download file: - * http://www.azillionmonkeys.com/qed/pstdint.h - * and change next line to: - * #include "pstdint.h" - */ -#include "pstdint.h" - -#define F_MODRM 0x00000001 -#define F_SIB 0x00000002 -#define F_IMM8 0x00000004 -#define F_IMM16 0x00000008 -#define F_IMM32 0x00000010 -#define F_DISP8 0x00000020 -#define F_DISP16 0x00000040 -#define F_DISP32 0x00000080 -#define F_RELATIVE 0x00000100 -#define F_2IMM16 0x00000800 -#define F_ERROR 0x00001000 -#define F_ERROR_OPCODE 0x00002000 -#define F_ERROR_LENGTH 0x00004000 -#define F_ERROR_LOCK 0x00008000 -#define F_ERROR_OPERAND 0x00010000 -#define F_PREFIX_REPNZ 0x01000000 -#define F_PREFIX_REPX 0x02000000 -#define F_PREFIX_REP 0x03000000 -#define F_PREFIX_66 0x04000000 -#define F_PREFIX_67 0x08000000 -#define F_PREFIX_LOCK 0x10000000 -#define F_PREFIX_SEG 0x20000000 -#define F_PREFIX_ANY 0x3f000000 - -#define PREFIX_SEGMENT_CS 0x2e -#define PREFIX_SEGMENT_SS 0x36 -#define PREFIX_SEGMENT_DS 0x3e -#define PREFIX_SEGMENT_ES 0x26 -#define PREFIX_SEGMENT_FS 0x64 -#define PREFIX_SEGMENT_GS 0x65 -#define PREFIX_LOCK 0xf0 -#define PREFIX_REPNZ 0xf2 -#define PREFIX_REPX 0xf3 -#define PREFIX_OPERAND_SIZE 0x66 -#define PREFIX_ADDRESS_SIZE 0x67 - -#pragma pack(push,1) - -typedef struct { - uint8_t len; - uint8_t p_rep; - uint8_t p_lock; - uint8_t p_seg; - uint8_t p_66; - uint8_t p_67; - uint8_t opcode; - uint8_t opcode2; - uint8_t modrm; - uint8_t modrm_mod; - uint8_t modrm_reg; - uint8_t modrm_rm; - uint8_t sib; - uint8_t sib_scale; - uint8_t sib_index; - uint8_t sib_base; - union { - uint8_t imm8; - uint16_t imm16; - uint32_t imm32; - } imm; - union { - uint8_t disp8; - uint16_t disp16; - uint32_t disp32; - } disp; - uint32_t flags; -} hde32s; - -#pragma pack(pop) - -#ifdef __cplusplus -extern "C" { -#endif - -/* __cdecl */ -unsigned int hde32_disasm(const void *code, hde32s *hs); - -#ifdef __cplusplus -} -#endif - -#endif /* _HDE32_H_ */ diff --git a/external/minhook/src/hde/hde64.c b/external/minhook/src/hde/hde64.c deleted file mode 100644 index 55a702e7..00000000 --- a/external/minhook/src/hde/hde64.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Hacker Disassembler Engine 64 C - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - */ - -#if defined(_M_X64) || defined(__x86_64__) - -#include -#include "hde64.h" -#include "table64.h" - -unsigned int hde64_disasm(const void *code, hde64s *hs) -{ - uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; - uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0; - uint8_t op64 = 0; - - memset(hs, 0, sizeof(hde64s)); - - for (x = 16; x; x--) - switch (c = *p++) { - case 0xf3: - hs->p_rep = c; - pref |= PRE_F3; - break; - case 0xf2: - hs->p_rep = c; - pref |= PRE_F2; - break; - case 0xf0: - hs->p_lock = c; - pref |= PRE_LOCK; - break; - case 0x26: case 0x2e: case 0x36: - case 0x3e: case 0x64: case 0x65: - hs->p_seg = c; - pref |= PRE_SEG; - break; - case 0x66: - hs->p_66 = c; - pref |= PRE_66; - break; - case 0x67: - hs->p_67 = c; - pref |= PRE_67; - break; - default: - goto pref_done; - } - pref_done: - - hs->flags = (uint32_t)pref << 23; - - if (!pref) - pref |= PRE_NONE; - - if ((c & 0xf0) == 0x40) { - hs->flags |= F_PREFIX_REX; - if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8) - op64++; - hs->rex_r = (c & 7) >> 2; - hs->rex_x = (c & 3) >> 1; - hs->rex_b = c & 1; - if (((c = *p++) & 0xf0) == 0x40) { - opcode = c; - goto error_opcode; - } - } - - if ((hs->opcode = c) == 0x0f) { - hs->opcode2 = c = *p++; - ht += DELTA_OPCODES; - } else if (c >= 0xa0 && c <= 0xa3) { - op64++; - if (pref & PRE_67) - pref |= PRE_66; - else - pref &= ~PRE_66; - } - - opcode = c; - cflags = ht[ht[opcode / 4] + (opcode % 4)]; - - if (cflags == C_ERROR) { - error_opcode: - hs->flags |= F_ERROR | F_ERROR_OPCODE; - cflags = 0; - if ((opcode & -3) == 0x24) - cflags++; - } - - x = 0; - if (cflags & C_GROUP) { - uint16_t t; - t = *(uint16_t *)(ht + (cflags & 0x7f)); - cflags = (uint8_t)t; - x = (uint8_t)(t >> 8); - } - - if (hs->opcode2) { - ht = hde64_table + DELTA_PREFIXES; - if (ht[ht[opcode / 4] + (opcode % 4)] & pref) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - } - - if (cflags & C_MODRM) { - hs->flags |= F_MODRM; - hs->modrm = c = *p++; - hs->modrm_mod = m_mod = c >> 6; - hs->modrm_rm = m_rm = c & 7; - hs->modrm_reg = m_reg = (c & 0x3f) >> 3; - - if (x && ((x << m_reg) & 0x80)) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - - if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { - uint8_t t = opcode - 0xd9; - if (m_mod == 3) { - ht = hde64_table + DELTA_FPU_MODRM + t*8; - t = ht[m_reg] << m_rm; - } else { - ht = hde64_table + DELTA_FPU_REG; - t = ht[t] << m_reg; - } - if (t & 0x80) - hs->flags |= F_ERROR | F_ERROR_OPCODE; - } - - if (pref & PRE_LOCK) { - if (m_mod == 3) { - hs->flags |= F_ERROR | F_ERROR_LOCK; - } else { - uint8_t *table_end, op = opcode; - if (hs->opcode2) { - ht = hde64_table + DELTA_OP2_LOCK_OK; - table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; - } else { - ht = hde64_table + DELTA_OP_LOCK_OK; - table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; - op &= -2; - } - for (; ht != table_end; ht++) - if (*ht++ == op) { - if (!((*ht << m_reg) & 0x80)) - goto no_lock_error; - else - break; - } - hs->flags |= F_ERROR | F_ERROR_LOCK; - no_lock_error: - ; - } - } - - if (hs->opcode2) { - switch (opcode) { - case 0x20: case 0x22: - m_mod = 3; - if (m_reg > 4 || m_reg == 1) - goto error_operand; - else - goto no_error_operand; - case 0x21: case 0x23: - m_mod = 3; - if (m_reg == 4 || m_reg == 5) - goto error_operand; - else - goto no_error_operand; - } - } else { - switch (opcode) { - case 0x8c: - if (m_reg > 5) - goto error_operand; - else - goto no_error_operand; - case 0x8e: - if (m_reg == 1 || m_reg > 5) - goto error_operand; - else - goto no_error_operand; - } - } - - if (m_mod == 3) { - uint8_t *table_end; - if (hs->opcode2) { - ht = hde64_table + DELTA_OP2_ONLY_MEM; - table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM; - } else { - ht = hde64_table + DELTA_OP_ONLY_MEM; - table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; - } - for (; ht != table_end; ht += 2) - if (*ht++ == opcode) { - if (*ht++ & pref && !((*ht << m_reg) & 0x80)) - goto error_operand; - else - break; - } - goto no_error_operand; - } else if (hs->opcode2) { - switch (opcode) { - case 0x50: case 0xd7: case 0xf7: - if (pref & (PRE_NONE | PRE_66)) - goto error_operand; - break; - case 0xd6: - if (pref & (PRE_F2 | PRE_F3)) - goto error_operand; - break; - case 0xc5: - goto error_operand; - } - goto no_error_operand; - } else - goto no_error_operand; - - error_operand: - hs->flags |= F_ERROR | F_ERROR_OPERAND; - no_error_operand: - - c = *p++; - if (m_reg <= 1) { - if (opcode == 0xf6) - cflags |= C_IMM8; - else if (opcode == 0xf7) - cflags |= C_IMM_P66; - } - - switch (m_mod) { - case 0: - if (pref & PRE_67) { - if (m_rm == 6) - disp_size = 2; - } else - if (m_rm == 5) - disp_size = 4; - break; - case 1: - disp_size = 1; - break; - case 2: - disp_size = 2; - if (!(pref & PRE_67)) - disp_size <<= 1; - } - - if (m_mod != 3 && m_rm == 4) { - hs->flags |= F_SIB; - p++; - hs->sib = c; - hs->sib_scale = c >> 6; - hs->sib_index = (c & 0x3f) >> 3; - if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) - disp_size = 4; - } - - p--; - switch (disp_size) { - case 1: - hs->flags |= F_DISP8; - hs->disp.disp8 = *p; - break; - case 2: - hs->flags |= F_DISP16; - hs->disp.disp16 = *(uint16_t *)p; - break; - case 4: - hs->flags |= F_DISP32; - hs->disp.disp32 = *(uint32_t *)p; - } - p += disp_size; - } else if (pref & PRE_LOCK) - hs->flags |= F_ERROR | F_ERROR_LOCK; - - if (cflags & C_IMM_P66) { - if (cflags & C_REL32) { - if (pref & PRE_66) { - hs->flags |= F_IMM16 | F_RELATIVE; - hs->imm.imm16 = *(uint16_t *)p; - p += 2; - goto disasm_done; - } - goto rel32_ok; - } - if (op64) { - hs->flags |= F_IMM64; - hs->imm.imm64 = *(uint64_t *)p; - p += 8; - } else if (!(pref & PRE_66)) { - hs->flags |= F_IMM32; - hs->imm.imm32 = *(uint32_t *)p; - p += 4; - } else - goto imm16_ok; - } - - - if (cflags & C_IMM16) { - imm16_ok: - hs->flags |= F_IMM16; - hs->imm.imm16 = *(uint16_t *)p; - p += 2; - } - if (cflags & C_IMM8) { - hs->flags |= F_IMM8; - hs->imm.imm8 = *p++; - } - - if (cflags & C_REL32) { - rel32_ok: - hs->flags |= F_IMM32 | F_RELATIVE; - hs->imm.imm32 = *(uint32_t *)p; - p += 4; - } else if (cflags & C_REL8) { - hs->flags |= F_IMM8 | F_RELATIVE; - hs->imm.imm8 = *p++; - } - - disasm_done: - - if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { - hs->flags |= F_ERROR | F_ERROR_LENGTH; - hs->len = 15; - } - - return (unsigned int)hs->len; -} - -#endif // defined(_M_X64) || defined(__x86_64__) diff --git a/external/minhook/src/hde/hde64.h b/external/minhook/src/hde/hde64.h deleted file mode 100644 index ecbf4df9..00000000 --- a/external/minhook/src/hde/hde64.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Hacker Disassembler Engine 64 - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - * hde64.h: C/C++ header file - * - */ - -#ifndef _HDE64_H_ -#define _HDE64_H_ - -/* stdint.h - C99 standard header - * http://en.wikipedia.org/wiki/stdint.h - * - * if your compiler doesn't contain "stdint.h" header (for - * example, Microsoft Visual C++), you can download file: - * http://www.azillionmonkeys.com/qed/pstdint.h - * and change next line to: - * #include "pstdint.h" - */ -#include "pstdint.h" - -#define F_MODRM 0x00000001 -#define F_SIB 0x00000002 -#define F_IMM8 0x00000004 -#define F_IMM16 0x00000008 -#define F_IMM32 0x00000010 -#define F_IMM64 0x00000020 -#define F_DISP8 0x00000040 -#define F_DISP16 0x00000080 -#define F_DISP32 0x00000100 -#define F_RELATIVE 0x00000200 -#define F_ERROR 0x00001000 -#define F_ERROR_OPCODE 0x00002000 -#define F_ERROR_LENGTH 0x00004000 -#define F_ERROR_LOCK 0x00008000 -#define F_ERROR_OPERAND 0x00010000 -#define F_PREFIX_REPNZ 0x01000000 -#define F_PREFIX_REPX 0x02000000 -#define F_PREFIX_REP 0x03000000 -#define F_PREFIX_66 0x04000000 -#define F_PREFIX_67 0x08000000 -#define F_PREFIX_LOCK 0x10000000 -#define F_PREFIX_SEG 0x20000000 -#define F_PREFIX_REX 0x40000000 -#define F_PREFIX_ANY 0x7f000000 - -#define PREFIX_SEGMENT_CS 0x2e -#define PREFIX_SEGMENT_SS 0x36 -#define PREFIX_SEGMENT_DS 0x3e -#define PREFIX_SEGMENT_ES 0x26 -#define PREFIX_SEGMENT_FS 0x64 -#define PREFIX_SEGMENT_GS 0x65 -#define PREFIX_LOCK 0xf0 -#define PREFIX_REPNZ 0xf2 -#define PREFIX_REPX 0xf3 -#define PREFIX_OPERAND_SIZE 0x66 -#define PREFIX_ADDRESS_SIZE 0x67 - -#pragma pack(push,1) - -typedef struct { - uint8_t len; - uint8_t p_rep; - uint8_t p_lock; - uint8_t p_seg; - uint8_t p_66; - uint8_t p_67; - uint8_t rex; - uint8_t rex_w; - uint8_t rex_r; - uint8_t rex_x; - uint8_t rex_b; - uint8_t opcode; - uint8_t opcode2; - uint8_t modrm; - uint8_t modrm_mod; - uint8_t modrm_reg; - uint8_t modrm_rm; - uint8_t sib; - uint8_t sib_scale; - uint8_t sib_index; - uint8_t sib_base; - union { - uint8_t imm8; - uint16_t imm16; - uint32_t imm32; - uint64_t imm64; - } imm; - union { - uint8_t disp8; - uint16_t disp16; - uint32_t disp32; - } disp; - uint32_t flags; -} hde64s; - -#pragma pack(pop) - -#ifdef __cplusplus -extern "C" { -#endif - -/* __cdecl */ -unsigned int hde64_disasm(const void *code, hde64s *hs); - -#ifdef __cplusplus -} -#endif - -#endif /* _HDE64_H_ */ diff --git a/external/minhook/src/hde/pstdint.h b/external/minhook/src/hde/pstdint.h deleted file mode 100644 index 84d82a01..00000000 --- a/external/minhook/src/hde/pstdint.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#include - -// Integer types for HDE. -typedef INT8 int8_t; -typedef INT16 int16_t; -typedef INT32 int32_t; -typedef INT64 int64_t; -typedef UINT8 uint8_t; -typedef UINT16 uint16_t; -typedef UINT32 uint32_t; -typedef UINT64 uint64_t; diff --git a/external/minhook/src/hde/table32.h b/external/minhook/src/hde/table32.h deleted file mode 100644 index 7b3e12e3..00000000 --- a/external/minhook/src/hde/table32.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Hacker Disassembler Engine 32 C - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - */ - -#define C_NONE 0x00 -#define C_MODRM 0x01 -#define C_IMM8 0x02 -#define C_IMM16 0x04 -#define C_IMM_P66 0x10 -#define C_REL8 0x20 -#define C_REL32 0x40 -#define C_GROUP 0x80 -#define C_ERROR 0xff - -#define PRE_ANY 0x00 -#define PRE_NONE 0x01 -#define PRE_F2 0x02 -#define PRE_F3 0x04 -#define PRE_66 0x08 -#define PRE_67 0x10 -#define PRE_LOCK 0x20 -#define PRE_SEG 0x40 -#define PRE_ALL 0xff - -#define DELTA_OPCODES 0x4a -#define DELTA_FPU_REG 0xf1 -#define DELTA_FPU_MODRM 0xf8 -#define DELTA_PREFIXES 0x130 -#define DELTA_OP_LOCK_OK 0x1a1 -#define DELTA_OP2_LOCK_OK 0x1b9 -#define DELTA_OP_ONLY_MEM 0x1cb -#define DELTA_OP2_ONLY_MEM 0x1da - -unsigned char hde32_table[] = { - 0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3, - 0xa8,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xac,0xaa,0xb2,0xaa,0x9f,0x9f, - 0x9f,0x9f,0xb5,0xa3,0xa3,0xa4,0xaa,0xaa,0xba,0xaa,0x96,0xaa,0xa8,0xaa,0xc3, - 0xc3,0x96,0x96,0xb7,0xae,0xd6,0xbd,0xa3,0xc5,0xa3,0xa3,0x9f,0xc3,0x9c,0xaa, - 0xaa,0xac,0xaa,0xbf,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0x90, - 0x82,0x7d,0x97,0x59,0x59,0x59,0x59,0x59,0x7f,0x59,0x59,0x60,0x7d,0x7f,0x7f, - 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x9a,0x88,0x7d, - 0x59,0x50,0x50,0x50,0x50,0x59,0x59,0x59,0x59,0x61,0x94,0x61,0x9e,0x59,0x59, - 0x85,0x59,0x92,0xa3,0x60,0x60,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59, - 0x59,0x59,0x9f,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xcc,0x01,0xbc,0x03,0xf0, - 0x10,0x10,0x10,0x10,0x50,0x50,0x50,0x50,0x14,0x20,0x20,0x20,0x20,0x01,0x01, - 0x01,0x01,0xc4,0x02,0x10,0x00,0x00,0x00,0x00,0x01,0x01,0xc0,0xc2,0x10,0x11, - 0x02,0x03,0x11,0x03,0x03,0x04,0x00,0x00,0x14,0x00,0x02,0x00,0x00,0xc6,0xc8, - 0x02,0x02,0x02,0x02,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xca, - 0x01,0x01,0x01,0x00,0x06,0x00,0x04,0x00,0xc0,0xc2,0x01,0x01,0x03,0x01,0xff, - 0xff,0x01,0x00,0x03,0xc4,0xc4,0xc6,0x03,0x01,0x01,0x01,0xff,0x03,0x03,0x03, - 0xc8,0x40,0x00,0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00, - 0x00,0x00,0x00,0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00, - 0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0xff,0xff,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x7f,0x00,0x00,0xff,0x4a,0x4a,0x4a,0x4a,0x4b,0x52,0x4a,0x4a,0x4a,0x4a,0x4f, - 0x4c,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x55,0x45,0x40,0x4a,0x4a,0x4a, - 0x45,0x59,0x4d,0x46,0x4a,0x5d,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a, - 0x4a,0x4a,0x4a,0x4a,0x4a,0x61,0x63,0x67,0x4e,0x4a,0x4a,0x6b,0x6d,0x4a,0x4a, - 0x45,0x6d,0x4a,0x4a,0x44,0x45,0x4a,0x4a,0x00,0x00,0x00,0x02,0x0d,0x06,0x06, - 0x06,0x06,0x0e,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x00,0x06,0x06,0x02,0x06, - 0x00,0x0a,0x0a,0x07,0x07,0x06,0x02,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, - 0x04,0x04,0x00,0x00,0x00,0x0e,0x05,0x06,0x06,0x06,0x01,0x06,0x00,0x00,0x08, - 0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01, - 0x86,0x00,0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba, - 0xf8,0xbb,0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00, - 0xc4,0xff,0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00, - 0x13,0x09,0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07, - 0xb2,0xff,0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf, - 0xe7,0x08,0x00,0xf0,0x02,0x00 -}; diff --git a/external/minhook/src/hde/table64.h b/external/minhook/src/hde/table64.h deleted file mode 100644 index 01d4541f..00000000 --- a/external/minhook/src/hde/table64.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Hacker Disassembler Engine 64 C - * Copyright (c) 2008-2009, Vyacheslav Patkov. - * All rights reserved. - * - */ - -#define C_NONE 0x00 -#define C_MODRM 0x01 -#define C_IMM8 0x02 -#define C_IMM16 0x04 -#define C_IMM_P66 0x10 -#define C_REL8 0x20 -#define C_REL32 0x40 -#define C_GROUP 0x80 -#define C_ERROR 0xff - -#define PRE_ANY 0x00 -#define PRE_NONE 0x01 -#define PRE_F2 0x02 -#define PRE_F3 0x04 -#define PRE_66 0x08 -#define PRE_67 0x10 -#define PRE_LOCK 0x20 -#define PRE_SEG 0x40 -#define PRE_ALL 0xff - -#define DELTA_OPCODES 0x4a -#define DELTA_FPU_REG 0xfd -#define DELTA_FPU_MODRM 0x104 -#define DELTA_PREFIXES 0x13c -#define DELTA_OP_LOCK_OK 0x1ae -#define DELTA_OP2_LOCK_OK 0x1c6 -#define DELTA_OP_ONLY_MEM 0x1d8 -#define DELTA_OP2_ONLY_MEM 0x1e7 - -unsigned char hde64_table[] = { - 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5, - 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1, - 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea, - 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0, - 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab, - 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92, - 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90, - 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b, - 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b, - 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc, - 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20, - 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff, - 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00, - 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01, - 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10, - 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00, - 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00, - 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, - 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00, - 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40, - 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43, - 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, - 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40, - 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06, - 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07, - 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, - 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10, - 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00, - 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb, - 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff, - 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09, - 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff, - 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08, - 0x00,0xf0,0x02,0x00 -}; diff --git a/external/minhook/src/hook.c b/external/minhook/src/hook.c deleted file mode 100644 index a1975893..00000000 --- a/external/minhook/src/hook.c +++ /dev/null @@ -1,906 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include - -#include "../include/MinHook.h" -#include "buffer.h" -#include "trampoline.h" - -#ifndef ARRAYSIZE - #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) -#endif - -// Initial capacity of the HOOK_ENTRY buffer. -#define INITIAL_HOOK_CAPACITY 32 - -// Initial capacity of the thread IDs buffer. -#define INITIAL_THREAD_CAPACITY 128 - -// Special hook position values. -#define INVALID_HOOK_POS UINT_MAX -#define ALL_HOOKS_POS UINT_MAX - -// Freeze() action argument defines. -#define ACTION_DISABLE 0 -#define ACTION_ENABLE 1 -#define ACTION_APPLY_QUEUED 2 - -// Thread access rights for suspending/resuming threads. -#define THREAD_ACCESS \ - (THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SET_CONTEXT) - -// Hook information. -typedef struct _HOOK_ENTRY -{ - LPVOID pTarget; // Address of the target function. - LPVOID pDetour; // Address of the detour or relay function. - LPVOID pTrampoline; // Address of the trampoline function. - UINT8 backup[8]; // Original prologue of the target function. - - UINT8 patchAbove : 1; // Uses the hot patch area. - UINT8 isEnabled : 1; // Enabled. - UINT8 queueEnable : 1; // Queued for enabling/disabling when != isEnabled. - - UINT nIP : 4; // Count of the instruction boundaries. - UINT8 oldIPs[8]; // Instruction boundaries of the target function. - UINT8 newIPs[8]; // Instruction boundaries of the trampoline function. -} HOOK_ENTRY, *PHOOK_ENTRY; - -// Suspended threads for Freeze()/Unfreeze(). -typedef struct _FROZEN_THREADS -{ - LPDWORD pItems; // Data heap - UINT capacity; // Size of allocated data heap, items - UINT size; // Actual number of data items -} FROZEN_THREADS, *PFROZEN_THREADS; - -//------------------------------------------------------------------------- -// Global Variables: -//------------------------------------------------------------------------- - -// Spin lock flag for EnterSpinLock()/LeaveSpinLock(). -volatile LONG g_isLocked = FALSE; - -// Private heap handle. If not NULL, this library is initialized. -HANDLE g_hHeap = NULL; - -// Hook entries. -struct -{ - PHOOK_ENTRY pItems; // Data heap - UINT capacity; // Size of allocated data heap, items - UINT size; // Actual number of data items -} g_hooks; - -//------------------------------------------------------------------------- -// Returns INVALID_HOOK_POS if not found. -static UINT FindHookEntry(LPVOID pTarget) -{ - UINT i; - for (i = 0; i < g_hooks.size; ++i) - { - if ((ULONG_PTR)pTarget == (ULONG_PTR)g_hooks.pItems[i].pTarget) - return i; - } - - return INVALID_HOOK_POS; -} - -//------------------------------------------------------------------------- -static PHOOK_ENTRY AddHookEntry() -{ - if (g_hooks.pItems == NULL) - { - g_hooks.capacity = INITIAL_HOOK_CAPACITY; - g_hooks.pItems = (PHOOK_ENTRY)HeapAlloc( - g_hHeap, 0, g_hooks.capacity * sizeof(HOOK_ENTRY)); - if (g_hooks.pItems == NULL) - return NULL; - } - else if (g_hooks.size >= g_hooks.capacity) - { - PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc( - g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity * 2) * sizeof(HOOK_ENTRY)); - if (p == NULL) - return NULL; - - g_hooks.capacity *= 2; - g_hooks.pItems = p; - } - - return &g_hooks.pItems[g_hooks.size++]; -} - -//------------------------------------------------------------------------- -static void DeleteHookEntry(UINT pos) -{ - if (pos < g_hooks.size - 1) - g_hooks.pItems[pos] = g_hooks.pItems[g_hooks.size - 1]; - - g_hooks.size--; - - if (g_hooks.capacity / 2 >= INITIAL_HOOK_CAPACITY && g_hooks.capacity / 2 >= g_hooks.size) - { - PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc( - g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity / 2) * sizeof(HOOK_ENTRY)); - if (p == NULL) - return; - - g_hooks.capacity /= 2; - g_hooks.pItems = p; - } -} - -//------------------------------------------------------------------------- -static DWORD_PTR FindOldIP(PHOOK_ENTRY pHook, DWORD_PTR ip) -{ - UINT i; - - if (pHook->patchAbove && ip == ((DWORD_PTR)pHook->pTarget - sizeof(JMP_REL))) - return (DWORD_PTR)pHook->pTarget; - - for (i = 0; i < pHook->nIP; ++i) - { - if (ip == ((DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i])) - return (DWORD_PTR)pHook->pTarget + pHook->oldIPs[i]; - } - -#if defined(_M_X64) || defined(__x86_64__) - // Check relay function. - if (ip == (DWORD_PTR)pHook->pDetour) - return (DWORD_PTR)pHook->pTarget; -#endif - - return 0; -} - -//------------------------------------------------------------------------- -static DWORD_PTR FindNewIP(PHOOK_ENTRY pHook, DWORD_PTR ip) -{ - UINT i; - for (i = 0; i < pHook->nIP; ++i) - { - if (ip == ((DWORD_PTR)pHook->pTarget + pHook->oldIPs[i])) - return (DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i]; - } - - return 0; -} - -//------------------------------------------------------------------------- -static void ProcessThreadIPs(HANDLE hThread, UINT pos, UINT action) -{ - // If the thread suspended in the overwritten area, - // move IP to the proper address. - - CONTEXT c; -#if defined(_M_X64) || defined(__x86_64__) - DWORD64 *pIP = &c.Rip; -#else - DWORD *pIP = &c.Eip; -#endif - UINT count; - - c.ContextFlags = CONTEXT_CONTROL; - if (!GetThreadContext(hThread, &c)) - return; - - if (pos == ALL_HOOKS_POS) - { - pos = 0; - count = g_hooks.size; - } - else - { - count = pos + 1; - } - - for (; pos < count; ++pos) - { - PHOOK_ENTRY pHook = &g_hooks.pItems[pos]; - BOOL enable; - DWORD_PTR ip; - - switch (action) - { - case ACTION_DISABLE: - enable = FALSE; - break; - - case ACTION_ENABLE: - enable = TRUE; - break; - - default: // ACTION_APPLY_QUEUED - enable = pHook->queueEnable; - break; - } - if (pHook->isEnabled == enable) - continue; - - if (enable) - ip = FindNewIP(pHook, *pIP); - else - ip = FindOldIP(pHook, *pIP); - - if (ip != 0) - { - *pIP = ip; - SetThreadContext(hThread, &c); - } - } -} - -//------------------------------------------------------------------------- -static VOID EnumerateThreads(PFROZEN_THREADS pThreads) -{ - HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); - if (hSnapshot != INVALID_HANDLE_VALUE) - { - THREADENTRY32 te; - te.dwSize = sizeof(THREADENTRY32); - if (Thread32First(hSnapshot, &te)) - { - do - { - if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(DWORD)) - && te.th32OwnerProcessID == GetCurrentProcessId() - && te.th32ThreadID != GetCurrentThreadId()) - { - if (pThreads->pItems == NULL) - { - pThreads->capacity = INITIAL_THREAD_CAPACITY; - pThreads->pItems - = (LPDWORD)HeapAlloc(g_hHeap, 0, pThreads->capacity * sizeof(DWORD)); - if (pThreads->pItems == NULL) - break; - } - else if (pThreads->size >= pThreads->capacity) - { - LPDWORD p = (LPDWORD)HeapReAlloc( - g_hHeap, 0, pThreads->pItems, (pThreads->capacity * 2) * sizeof(DWORD)); - if (p == NULL) - { - HeapFree(g_hHeap, 0, pThreads->pItems); - pThreads->pItems = NULL; - break; - } - - pThreads->capacity *= 2; - pThreads->pItems = p; - } - pThreads->pItems[pThreads->size++] = te.th32ThreadID; - } - - te.dwSize = sizeof(THREADENTRY32); - } while (Thread32Next(hSnapshot, &te)); - } - CloseHandle(hSnapshot); - } -} - -//------------------------------------------------------------------------- -static MH_STATUS Freeze(PFROZEN_THREADS pThreads, UINT pos, UINT action) -{ - pThreads->pItems = NULL; - pThreads->capacity = 0; - pThreads->size = 0; - EnumerateThreads(pThreads); - - MH_STATUS status = MH_OK; - - if (pThreads->pItems != NULL) - { - UINT i; - for (i = 0; i < pThreads->size; ++i) - { - HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]); - if (hThread != NULL) - { - SuspendThread(hThread); - ProcessThreadIPs(hThread, pos, action); - CloseHandle(hThread); - } - } - } - else - { - status = MH_ERROR_MEMORY_ALLOC; - } - - return status; -} - -//------------------------------------------------------------------------- -static VOID Unfreeze(PFROZEN_THREADS pThreads) -{ - UINT i; - for (i = 0; i < pThreads->size; ++i) - { - HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]); - if (hThread != NULL) - { - ResumeThread(hThread); - CloseHandle(hThread); - } - } - - HeapFree(g_hHeap, 0, pThreads->pItems); -} - -//------------------------------------------------------------------------- -static MH_STATUS EnableHookLL(UINT pos, BOOL enable) -{ - PHOOK_ENTRY pHook = &g_hooks.pItems[pos]; - DWORD oldProtect; - SIZE_T patchSize = sizeof(JMP_REL); - LPBYTE pPatchTarget = (LPBYTE)pHook->pTarget; - - if (pHook->patchAbove) - { - pPatchTarget -= sizeof(JMP_REL); - patchSize += sizeof(JMP_REL_SHORT); - } - - if (!VirtualProtect(pPatchTarget, patchSize, PAGE_EXECUTE_READWRITE, &oldProtect)) - return MH_ERROR_MEMORY_PROTECT; - - if (enable) - { - PJMP_REL pJmp = (PJMP_REL)pPatchTarget; - pJmp->opcode = 0xE9; - pJmp->operand = (UINT32)((LPBYTE)pHook->pDetour - (pPatchTarget + sizeof(JMP_REL))); - - if (pHook->patchAbove) - { - PJMP_REL_SHORT pShortJmp = (PJMP_REL_SHORT)pHook->pTarget; - pShortJmp->opcode = 0xEB; - pShortJmp->operand = (UINT8)(0 - (sizeof(JMP_REL_SHORT) + sizeof(JMP_REL))); - } - } - else - { - if (pHook->patchAbove) - memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); - else - memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL)); - } - - VirtualProtect(pPatchTarget, patchSize, oldProtect, &oldProtect); - - // Just-in-case measure. - FlushInstructionCache(GetCurrentProcess(), pPatchTarget, patchSize); - - pHook->isEnabled = enable; - pHook->queueEnable = enable; - - return MH_OK; -} - -//------------------------------------------------------------------------- -static MH_STATUS EnableAllHooksLL(BOOL enable) -{ - MH_STATUS status = MH_OK; - UINT i, first = INVALID_HOOK_POS; - - for (i = 0; i < g_hooks.size; ++i) - { - if (g_hooks.pItems[i].isEnabled != enable) - { - first = i; - break; - } - } - - if (first != INVALID_HOOK_POS) - { - FROZEN_THREADS threads; - status = Freeze(&threads, ALL_HOOKS_POS, enable ? ACTION_ENABLE : ACTION_DISABLE); - if (status == MH_OK) - { - for (i = first; i < g_hooks.size; ++i) - { - if (g_hooks.pItems[i].isEnabled != enable) - { - status = EnableHookLL(i, enable); - if (status != MH_OK) - break; - } - } - - Unfreeze(&threads); - } - } - - return status; -} - -//------------------------------------------------------------------------- -static VOID EnterSpinLock(VOID) -{ - SIZE_T spinCount = 0; - - // Wait until the flag is FALSE. - while (InterlockedCompareExchange(&g_isLocked, TRUE, FALSE) != FALSE) - { - // No need to generate a memory barrier here, since InterlockedCompareExchange() - // generates a full memory barrier itself. - - // Prevent the loop from being too busy. - if (spinCount < 32) - Sleep(0); - else - Sleep(1); - - spinCount++; - } -} - -//------------------------------------------------------------------------- -static VOID LeaveSpinLock(VOID) -{ - // No need to generate a memory barrier here, since InterlockedExchange() - // generates a full memory barrier itself. - - InterlockedExchange(&g_isLocked, FALSE); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_Initialize(VOID) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap == NULL) - { - g_hHeap = HeapCreate(0, 0, 0); - if (g_hHeap != NULL) - { - // Initialize the internal function buffer. - InitializeBuffer(); - } - else - { - status = MH_ERROR_MEMORY_ALLOC; - } - } - else - { - status = MH_ERROR_ALREADY_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_Uninitialize(VOID) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - status = EnableAllHooksLL(FALSE); - if (status == MH_OK) - { - // Free the internal function buffer. - - // HeapFree is actually not required, but some tools detect a false - // memory leak without HeapFree. - - UninitializeBuffer(); - - HeapFree(g_hHeap, 0, g_hooks.pItems); - HeapDestroy(g_hHeap); - - g_hHeap = NULL; - - g_hooks.pItems = NULL; - g_hooks.capacity = 0; - g_hooks.size = 0; - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - if (IsExecutableAddress(pTarget) && IsExecutableAddress(pDetour)) - { - UINT pos = FindHookEntry(pTarget); - if (pos == INVALID_HOOK_POS) - { - LPVOID pBuffer = AllocateBuffer(pTarget); - if (pBuffer != NULL) - { - TRAMPOLINE ct; - - ct.pTarget = pTarget; - ct.pDetour = pDetour; - ct.pTrampoline = pBuffer; - if (CreateTrampolineFunction(&ct)) - { - PHOOK_ENTRY pHook = AddHookEntry(); - if (pHook != NULL) - { - pHook->pTarget = ct.pTarget; -#if defined(_M_X64) || defined(__x86_64__) - pHook->pDetour = ct.pRelay; -#else - pHook->pDetour = ct.pDetour; -#endif - pHook->pTrampoline = ct.pTrampoline; - pHook->patchAbove = ct.patchAbove; - pHook->isEnabled = FALSE; - pHook->queueEnable = FALSE; - pHook->nIP = ct.nIP; - memcpy(pHook->oldIPs, ct.oldIPs, ARRAYSIZE(ct.oldIPs)); - memcpy(pHook->newIPs, ct.newIPs, ARRAYSIZE(ct.newIPs)); - - // Back up the target function. - - if (ct.patchAbove) - { - memcpy( - pHook->backup, - (LPBYTE)pTarget - sizeof(JMP_REL), - sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); - } - else - { - memcpy(pHook->backup, pTarget, sizeof(JMP_REL)); - } - - if (ppOriginal != NULL) - *ppOriginal = pHook->pTrampoline; - } - else - { - status = MH_ERROR_MEMORY_ALLOC; - } - } - else - { - status = MH_ERROR_UNSUPPORTED_FUNCTION; - } - - if (status != MH_OK) - { - FreeBuffer(pBuffer); - } - } - else - { - status = MH_ERROR_MEMORY_ALLOC; - } - } - else - { - status = MH_ERROR_ALREADY_CREATED; - } - } - else - { - status = MH_ERROR_NOT_EXECUTABLE; - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - UINT pos = FindHookEntry(pTarget); - if (pos != INVALID_HOOK_POS) - { - if (g_hooks.pItems[pos].isEnabled) - { - FROZEN_THREADS threads; - status = Freeze(&threads, pos, ACTION_DISABLE); - if (status == MH_OK) - { - status = EnableHookLL(pos, FALSE); - - Unfreeze(&threads); - } - } - - if (status == MH_OK) - { - FreeBuffer(g_hooks.pItems[pos].pTrampoline); - DeleteHookEntry(pos); - } - } - else - { - status = MH_ERROR_NOT_CREATED; - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -static MH_STATUS EnableHook(LPVOID pTarget, BOOL enable) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - if (pTarget == MH_ALL_HOOKS) - { - status = EnableAllHooksLL(enable); - } - else - { - UINT pos = FindHookEntry(pTarget); - if (pos != INVALID_HOOK_POS) - { - if (g_hooks.pItems[pos].isEnabled != enable) - { - FROZEN_THREADS threads; - status = Freeze(&threads, pos, ACTION_ENABLE); - if (status == MH_OK) - { - status = EnableHookLL(pos, enable); - - Unfreeze(&threads); - } - } - else - { - status = enable ? MH_ERROR_ENABLED : MH_ERROR_DISABLED; - } - } - else - { - status = MH_ERROR_NOT_CREATED; - } - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget) -{ - return EnableHook(pTarget, TRUE); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget) -{ - return EnableHook(pTarget, FALSE); -} - -//------------------------------------------------------------------------- -static MH_STATUS QueueHook(LPVOID pTarget, BOOL queueEnable) -{ - MH_STATUS status = MH_OK; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - if (pTarget == MH_ALL_HOOKS) - { - UINT i; - for (i = 0; i < g_hooks.size; ++i) - g_hooks.pItems[i].queueEnable = queueEnable; - } - else - { - UINT pos = FindHookEntry(pTarget); - if (pos != INVALID_HOOK_POS) - { - g_hooks.pItems[pos].queueEnable = queueEnable; - } - else - { - status = MH_ERROR_NOT_CREATED; - } - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget) -{ - return QueueHook(pTarget, TRUE); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget) -{ - return QueueHook(pTarget, FALSE); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_ApplyQueued(VOID) -{ - MH_STATUS status = MH_OK; - UINT i, first = INVALID_HOOK_POS; - - EnterSpinLock(); - - if (g_hHeap != NULL) - { - for (i = 0; i < g_hooks.size; ++i) - { - if (g_hooks.pItems[i].isEnabled != g_hooks.pItems[i].queueEnable) - { - first = i; - break; - } - } - - if (first != INVALID_HOOK_POS) - { - FROZEN_THREADS threads; - status = Freeze(&threads, ALL_HOOKS_POS, ACTION_APPLY_QUEUED); - if (status == MH_OK) - { - for (i = first; i < g_hooks.size; ++i) - { - PHOOK_ENTRY pHook = &g_hooks.pItems[i]; - if (pHook->isEnabled != pHook->queueEnable) - { - status = EnableHookLL(i, pHook->queueEnable); - if (status != MH_OK) - break; - } - } - - Unfreeze(&threads); - } - } - } - else - { - status = MH_ERROR_NOT_INITIALIZED; - } - - LeaveSpinLock(); - - return status; -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_CreateHookApiEx( - LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, - LPVOID *ppOriginal, LPVOID *ppTarget) -{ - HMODULE hModule; - LPVOID pTarget; - - hModule = GetModuleHandleW(pszModule); - if (hModule == NULL) - return MH_ERROR_MODULE_NOT_FOUND; - - pTarget = (LPVOID)GetProcAddress(hModule, pszProcName); - if (pTarget == NULL) - return MH_ERROR_FUNCTION_NOT_FOUND; - - if(ppTarget != NULL) - *ppTarget = pTarget; - - return MH_CreateHook(pTarget, pDetour, ppOriginal); -} - -//------------------------------------------------------------------------- -MH_STATUS WINAPI MH_CreateHookApi( - LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal) -{ - return MH_CreateHookApiEx(pszModule, pszProcName, pDetour, ppOriginal, NULL); -} - -//------------------------------------------------------------------------- -const char * WINAPI MH_StatusToString(MH_STATUS status) -{ -#define MH_ST2STR(x) \ - case x: \ - return #x; - - switch (status) { - MH_ST2STR(MH_UNKNOWN) - MH_ST2STR(MH_OK) - MH_ST2STR(MH_ERROR_ALREADY_INITIALIZED) - MH_ST2STR(MH_ERROR_NOT_INITIALIZED) - MH_ST2STR(MH_ERROR_ALREADY_CREATED) - MH_ST2STR(MH_ERROR_NOT_CREATED) - MH_ST2STR(MH_ERROR_ENABLED) - MH_ST2STR(MH_ERROR_DISABLED) - MH_ST2STR(MH_ERROR_NOT_EXECUTABLE) - MH_ST2STR(MH_ERROR_UNSUPPORTED_FUNCTION) - MH_ST2STR(MH_ERROR_MEMORY_ALLOC) - MH_ST2STR(MH_ERROR_MEMORY_PROTECT) - MH_ST2STR(MH_ERROR_MODULE_NOT_FOUND) - MH_ST2STR(MH_ERROR_FUNCTION_NOT_FOUND) - } - -#undef MH_ST2STR - - return "(unknown)"; -} diff --git a/external/minhook/src/trampoline.c b/external/minhook/src/trampoline.c deleted file mode 100644 index 617baf3f..00000000 --- a/external/minhook/src/trampoline.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include - -#ifdef _MSC_VER - #include -#endif - -#ifndef ARRAYSIZE - #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) -#endif - -#if defined(_M_X64) || defined(__x86_64__) - #include "./hde/hde64.h" - typedef hde64s HDE; - #define HDE_DISASM(code, hs) hde64_disasm(code, hs) -#else - #include "./hde/hde32.h" - typedef hde32s HDE; - #define HDE_DISASM(code, hs) hde32_disasm(code, hs) -#endif - -#include "trampoline.h" -#include "buffer.h" - -// Maximum size of a trampoline function. -#if defined(_M_X64) || defined(__x86_64__) - #define TRAMPOLINE_MAX_SIZE (MEMORY_SLOT_SIZE - sizeof(JMP_ABS)) -#else - #define TRAMPOLINE_MAX_SIZE MEMORY_SLOT_SIZE -#endif - -//------------------------------------------------------------------------- -static BOOL IsCodePadding(LPBYTE pInst, UINT size) -{ - UINT i; - - if (pInst[0] != 0x00 && pInst[0] != 0x90 && pInst[0] != 0xCC) - return FALSE; - - for (i = 1; i < size; ++i) - { - if (pInst[i] != pInst[0]) - return FALSE; - } - return TRUE; -} - -//------------------------------------------------------------------------- -BOOL CreateTrampolineFunction(PTRAMPOLINE ct) -{ -#if defined(_M_X64) || defined(__x86_64__) - CALL_ABS call = { - 0xFF, 0x15, 0x00000002, // FF15 00000002: CALL [RIP+8] - 0xEB, 0x08, // EB 08: JMP +10 - 0x0000000000000000ULL // Absolute destination address - }; - JMP_ABS jmp = { - 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6] - 0x0000000000000000ULL // Absolute destination address - }; - JCC_ABS jcc = { - 0x70, 0x0E, // 7* 0E: J** +16 - 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6] - 0x0000000000000000ULL // Absolute destination address - }; -#else - CALL_REL call = { - 0xE8, // E8 xxxxxxxx: CALL +5+xxxxxxxx - 0x00000000 // Relative destination address - }; - JMP_REL jmp = { - 0xE9, // E9 xxxxxxxx: JMP +5+xxxxxxxx - 0x00000000 // Relative destination address - }; - JCC_REL jcc = { - 0x0F, 0x80, // 0F8* xxxxxxxx: J** +6+xxxxxxxx - 0x00000000 // Relative destination address - }; -#endif - - UINT8 oldPos = 0; - UINT8 newPos = 0; - ULONG_PTR jmpDest = 0; // Destination address of an internal jump. - BOOL finished = FALSE; // Is the function completed? -#if defined(_M_X64) || defined(__x86_64__) - UINT8 instBuf[16]; -#endif - - ct->patchAbove = FALSE; - ct->nIP = 0; - - do - { - HDE hs; - UINT copySize; - LPVOID pCopySrc; - ULONG_PTR pOldInst = (ULONG_PTR)ct->pTarget + oldPos; - ULONG_PTR pNewInst = (ULONG_PTR)ct->pTrampoline + newPos; - - copySize = HDE_DISASM((LPVOID)pOldInst, &hs); - if (hs.flags & F_ERROR) - return FALSE; - - pCopySrc = (LPVOID)pOldInst; - if (oldPos >= sizeof(JMP_REL)) - { - // The trampoline function is long enough. - // Complete the function with the jump to the target function. -#if defined(_M_X64) || defined(__x86_64__) - jmp.address = pOldInst; -#else - jmp.operand = (UINT32)(pOldInst - (pNewInst + sizeof(jmp))); -#endif - pCopySrc = &jmp; - copySize = sizeof(jmp); - - finished = TRUE; - } -#if defined(_M_X64) || defined(__x86_64__) - else if ((hs.modrm & 0xC7) == 0x05) - { - // Instructions using RIP relative addressing. (ModR/M = 00???101B) - - // Modify the RIP relative address. - PUINT32 pRelAddr; - - // Avoid using memcpy to reduce the footprint. -#ifndef _MSC_VER - memcpy(instBuf, (LPBYTE)pOldInst, copySize); -#else - __movsb(instBuf, (LPBYTE)pOldInst, copySize); -#endif - pCopySrc = instBuf; - - // Relative address is stored at (instruction length - immediate value length - 4). - pRelAddr = (PUINT32)(instBuf + hs.len - ((hs.flags & 0x3C) >> 2) - 4); - *pRelAddr - = (UINT32)((pOldInst + hs.len + (INT32)hs.disp.disp32) - (pNewInst + hs.len)); - - // Complete the function if JMP (FF /4). - if (hs.opcode == 0xFF && hs.modrm_reg == 4) - finished = TRUE; - } -#endif - else if (hs.opcode == 0xE8) - { - // Direct relative CALL - ULONG_PTR dest = pOldInst + hs.len + (INT32)hs.imm.imm32; -#if defined(_M_X64) || defined(__x86_64__) - call.address = dest; -#else - call.operand = (UINT32)(dest - (pNewInst + sizeof(call))); -#endif - pCopySrc = &call; - copySize = sizeof(call); - } - else if ((hs.opcode & 0xFD) == 0xE9) - { - // Direct relative JMP (EB or E9) - ULONG_PTR dest = pOldInst + hs.len; - - if (hs.opcode == 0xEB) // isShort jmp - dest += (INT8)hs.imm.imm8; - else - dest += (INT32)hs.imm.imm32; - - // Simply copy an internal jump. - if ((ULONG_PTR)ct->pTarget <= dest - && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) - { - if (jmpDest < dest) - jmpDest = dest; - } - else - { -#if defined(_M_X64) || defined(__x86_64__) - jmp.address = dest; -#else - jmp.operand = (UINT32)(dest - (pNewInst + sizeof(jmp))); -#endif - pCopySrc = &jmp; - copySize = sizeof(jmp); - - // Exit the function if it is not in the branch. - finished = (pOldInst >= jmpDest); - } - } - else if ((hs.opcode & 0xF0) == 0x70 - || (hs.opcode & 0xFC) == 0xE0 - || (hs.opcode2 & 0xF0) == 0x80) - { - // Direct relative Jcc - ULONG_PTR dest = pOldInst + hs.len; - - if ((hs.opcode & 0xF0) == 0x70 // Jcc - || (hs.opcode & 0xFC) == 0xE0) // LOOPNZ/LOOPZ/LOOP/JECXZ - dest += (INT8)hs.imm.imm8; - else - dest += (INT32)hs.imm.imm32; - - // Simply copy an internal jump. - if ((ULONG_PTR)ct->pTarget <= dest - && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) - { - if (jmpDest < dest) - jmpDest = dest; - } - else if ((hs.opcode & 0xFC) == 0xE0) - { - // LOOPNZ/LOOPZ/LOOP/JCXZ/JECXZ to the outside are not supported. - return FALSE; - } - else - { - UINT8 cond = ((hs.opcode != 0x0F ? hs.opcode : hs.opcode2) & 0x0F); -#if defined(_M_X64) || defined(__x86_64__) - // Invert the condition in x64 mode to simplify the conditional jump logic. - jcc.opcode = 0x71 ^ cond; - jcc.address = dest; -#else - jcc.opcode1 = 0x80 | cond; - jcc.operand = (UINT32)(dest - (pNewInst + sizeof(jcc))); -#endif - pCopySrc = &jcc; - copySize = sizeof(jcc); - } - } - else if ((hs.opcode & 0xFE) == 0xC2) - { - // RET (C2 or C3) - - // Complete the function if not in a branch. - finished = (pOldInst >= jmpDest); - } - - // Can't alter the instruction length in a branch. - if (pOldInst < jmpDest && copySize != hs.len) - return FALSE; - - // Trampoline function is too large. - if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE) - return FALSE; - - // Trampoline function has too many instructions. - if (ct->nIP >= ARRAYSIZE(ct->oldIPs)) - return FALSE; - - ct->oldIPs[ct->nIP] = oldPos; - ct->newIPs[ct->nIP] = newPos; - ct->nIP++; - - // Avoid using memcpy to reduce the footprint. -#ifndef _MSC_VER - memcpy((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize); -#else - __movsb((LPBYTE)ct->pTrampoline + newPos, (LPBYTE)pCopySrc, copySize); -#endif - newPos += copySize; - oldPos += hs.len; - } - while (!finished); - - // Is there enough place for a long jump? - if (oldPos < sizeof(JMP_REL) - && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL) - oldPos)) - { - // Is there enough place for a short jump? - if (oldPos < sizeof(JMP_REL_SHORT) - && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL_SHORT) - oldPos)) - { - return FALSE; - } - - // Can we place the long jump above the function? - if (!IsExecutableAddress((LPBYTE)ct->pTarget - sizeof(JMP_REL))) - return FALSE; - - if (!IsCodePadding((LPBYTE)ct->pTarget - sizeof(JMP_REL), sizeof(JMP_REL))) - return FALSE; - - ct->patchAbove = TRUE; - } - -#if defined(_M_X64) || defined(__x86_64__) - // Create a relay function. - jmp.address = (ULONG_PTR)ct->pDetour; - - ct->pRelay = (LPBYTE)ct->pTrampoline + newPos; - memcpy(ct->pRelay, &jmp, sizeof(jmp)); -#endif - - return TRUE; -} diff --git a/external/minhook/src/trampoline.h b/external/minhook/src/trampoline.h deleted file mode 100644 index bdffdac0..00000000 --- a/external/minhook/src/trampoline.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#pragma pack(push, 1) - -// Structs for writing x86/x64 instructions. - -// 8-bit relative jump. -typedef struct _JMP_REL_SHORT -{ - UINT8 opcode; // EB xx: JMP +2+xx - UINT8 operand; -} JMP_REL_SHORT, *PJMP_REL_SHORT; - -// 32-bit direct relative jump/call. -typedef struct _JMP_REL -{ - UINT8 opcode; // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx - UINT32 operand; // Relative destination address -} JMP_REL, *PJMP_REL, CALL_REL; - -// 64-bit indirect absolute jump. -typedef struct _JMP_ABS -{ - UINT8 opcode0; // FF25 00000000: JMP [+6] - UINT8 opcode1; - UINT32 dummy; - UINT64 address; // Absolute destination address -} JMP_ABS, *PJMP_ABS; - -// 64-bit indirect absolute call. -typedef struct _CALL_ABS -{ - UINT8 opcode0; // FF15 00000002: CALL [+6] - UINT8 opcode1; - UINT32 dummy0; - UINT8 dummy1; // EB 08: JMP +10 - UINT8 dummy2; - UINT64 address; // Absolute destination address -} CALL_ABS; - -// 32-bit direct relative conditional jumps. -typedef struct _JCC_REL -{ - UINT8 opcode0; // 0F8* xxxxxxxx: J** +6+xxxxxxxx - UINT8 opcode1; - UINT32 operand; // Relative destination address -} JCC_REL; - -// 64bit indirect absolute conditional jumps that x64 lacks. -typedef struct _JCC_ABS -{ - UINT8 opcode; // 7* 0E: J** +16 - UINT8 dummy0; - UINT8 dummy1; // FF25 00000000: JMP [+6] - UINT8 dummy2; - UINT32 dummy3; - UINT64 address; // Absolute destination address -} JCC_ABS; - -#pragma pack(pop) - -typedef struct _TRAMPOLINE -{ - LPVOID pTarget; // [In] Address of the target function. - LPVOID pDetour; // [In] Address of the detour function. - LPVOID pTrampoline; // [In] Buffer address for the trampoline and relay function. - -#if defined(_M_X64) || defined(__x86_64__) - LPVOID pRelay; // [Out] Address of the relay function. -#endif - BOOL patchAbove; // [Out] Should use the hot patch area? - UINT nIP; // [Out] Number of the instruction boundaries. - UINT8 oldIPs[8]; // [Out] Instruction boundaries of the target function. - UINT8 newIPs[8]; // [Out] Instruction boundaries of the trampoline function. -} TRAMPOLINE, *PTRAMPOLINE; - -BOOL CreateTrampolineFunction(PTRAMPOLINE ct); diff --git a/license/lzham/LICENSE.txt b/license/lzham/LICENSE.txt new file mode 100644 index 00000000..94cf6adc --- /dev/null +++ b/license/lzham/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2009-2011 Richard Geldreich, Jr. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/license/minhook/AUTHORS.txt b/license/minhook/AUTHORS.txt deleted file mode 100644 index ebef1a6c..00000000 --- a/license/minhook/AUTHORS.txt +++ /dev/null @@ -1,8 +0,0 @@ -Tsuda Kageyu - Creator, maintainer - -Michael Maltsev - Added "Queue" functions. A lot of bug fixes. - -Andrey Unis - Rewrote the hook engine in plain C. diff --git a/license/minhook/LICENSE.txt b/license/minhook/LICENSE.txt deleted file mode 100644 index 74dea272..00000000 --- a/license/minhook/LICENSE.txt +++ /dev/null @@ -1,81 +0,0 @@ -MinHook - The Minimalistic API Hooking Library for x64/x86 -Copyright (C) 2009-2017 Tsuda Kageyu. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -================================================================================ -Portions of this software are Copyright (c) 2008-2009, Vyacheslav Patkov. -================================================================================ -Hacker Disassembler Engine 32 C -Copyright (c) 2008-2009, Vyacheslav Patkov. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -------------------------------------------------------------------------------- -Hacker Disassembler Engine 64 C -Copyright (c) 2008-2009, Vyacheslav Patkov. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/r5dedicated/console.cpp b/r5dedicated/console.cpp deleted file mode 100644 index 6cdc996a..00000000 --- a/r5dedicated/console.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "opcodes.h" -#include "console.h" - -//############################################################################# -// INITIALIZATION -//############################################################################# - -void SetupConsole() -{ - /////////////////////////////////////////////////////////////////////////// - // Create the console window - if (AllocConsole() == FALSE) - { - OutputDebugString("Failed to create console window!\n"); - return; - } - - /////////////////////////////////////////////////////////////////////////// - // Set the window title - FILE* sBuildTxt; - CHAR sBuildBuf[1024] = { 0 }; - - fopen_s(&sBuildTxt, "build.txt", "r"); - if (sBuildTxt) - { - while (fgets(sBuildBuf, sizeof(sBuildBuf), sBuildTxt) != NULL) - { - fclose(sBuildTxt); - } - } - SetConsoleTitle(sBuildBuf); - - /////////////////////////////////////////////////////////////////////////// - // Open input/output streams - FILE* fDummy; - freopen_s(&fDummy, "CONIN$", "r", stdin); - freopen_s(&fDummy, "CONOUT$", "w", stdout); - freopen_s(&fDummy, "CONOUT$", "w", stderr); - - /////////////////////////////////////////////////////////////////////////// - // Create a worker thread to process console commands - DWORD threadId0; - DWORD __stdcall ProcessConsoleWorker(LPVOID); - HANDLE hThread0 = CreateThread(NULL, 0, ProcessConsoleWorker, NULL, 0, &threadId0); - - if (hThread0) - { - printf("THREAD ID: %ld\n\n", threadId0); - CloseHandle(hThread0); - } -} - -//############################################################################# -// WORKER THREAD -//############################################################################# - -DWORD __stdcall ProcessConsoleWorker(LPVOID) -{ - // Loop forever - while (true) - { - std::string sCommand; - - /////////////////////////////////////////////////////////////////////// - // Get the user input on the debug console - printf(">"); - std::getline(std::cin, sCommand); - - /////////////////////////////////////////////////////////////////////// - // Engine toggles - if (sCommand == "toggle net") { Hooks::ToggleNetTrace(); continue; } - if (sCommand == "toggle dev") { Hooks::ToggleDevCommands(); continue; } - if (sCommand == "toggle fal") { g_bReturnAllFalse = !g_bReturnAllFalse; continue; } - /////////////////////////////////////////////////////////////////////// - // Debug toggles - if (sCommand == "pattern test") { PrintHAddress(); /*PrintOAddress();*/ continue; } - if (sCommand == "console test") { g_bDebugConsole = !g_bDebugConsole; continue; } - /////////////////////////////////////////////////////////////////////// - // Exec toggles - if (sCommand == "1") { addr_CommandExecute(NULL, "exec autoexec_dev"); } - if (sCommand == "2") { g_bDebugLoading = !g_bDebugLoading; continue; } - if (sCommand == "3") { SetCHostState(); continue; } // TEST - - /////////////////////////////////////////////////////////////////////// - // Execute the command in the r5 SQVM - addr_CommandExecute(NULL, sCommand.c_str()); - sCommand.clear(); - - /////////////////////////////////////////////////////////////////////// - // Sleep and loop - Sleep(50); - } - - return 0; -} \ No newline at end of file diff --git a/r5dedicated/console.h b/r5dedicated/console.h deleted file mode 100644 index 19d16a6b..00000000 --- a/r5dedicated/console.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -#include "pch.h" - -void SetupConsole(); -void ToggleDevCommands(); diff --git a/r5dedicated/csourceappsystemgroup.cpp b/r5dedicated/csourceappsystemgroup.cpp deleted file mode 100644 index bffa7cc3..00000000 --- a/r5dedicated/csourceappsystemgroup.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - CSourceAppSystemGroup_CreateFn originalCSourceAppSystemGroup_Create = nullptr; -} -auto g_bIsDedicated = (uint8_t*)0x162C61208; - -//----------------------------------------------------------------------------- -// Purpose: sets 'EbisuSDK' globals required in certain engine callbacks. -//----------------------------------------------------------------------------- -void HEbisuSDK_Init() -{ - auto ofs000 = (uint8_t*)0x1634F1690; - auto ofs001 = (uint8_t*)0x1634F16B0; - auto ofs002 = (uint8_t*)0x1634F1695; - auto ofs003 = (uint8_t*)0x1634F30D8; - auto ofs004 = (uint8_t*)0x1634F31D8; - - *(char*)(ofs000) = (char)0x1; // <-- | 1st EbisuSDK boolean to be checked. - *(char*)(ofs001) = (char)0x1; // <-- | 2nd EbisuSDK boolean to be checked. - *(char*)(ofs002) = (char)0x1; // <-- | 3rd EbisuSDK boolean to be checked. - *(char*)(ofs003) = (char)0x1; // <-- | Gets tested on listenserver for certain ConCommands. - *(char*)(ofs004) = (char)0x0; // <-- | TODO: enforces Necleus cvars when not equal to NULL. -} - -//----------------------------------------------------------------------------- -// Purpose: hook 'SourceAppSystemGroup::Create' and set m_bIsDedicated to true. -//----------------------------------------------------------------------------- -char __fastcall Hooks::CSourceAppSystemGroup_Create(__int64 a1) -{ - *g_bIsDedicated = 1; // HAS TO BE HERE!!! - HEbisuSDK_Init(); - return originalCSourceAppSystemGroup_Create(a1); -} \ No newline at end of file diff --git a/r5dedicated/cvengineserver.cpp b/r5dedicated/cvengineserver.cpp deleted file mode 100644 index 450639f4..00000000 --- a/r5dedicated/cvengineserver.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - IsPersistenceDataAvailableFn originalIsPersistenceDataAvailable = nullptr; -} - -//----------------------------------------------------------------------------- -// Sets the persistence var in the playerstruct to ready for each client -//----------------------------------------------------------------------------- -bool Hooks::IsPersistenceDataAvailable(__int64 thisptr, int client) -{ - static bool isPersistenceVarSet[256]; - - // TODO: Maybe not hardcode - std::uintptr_t playerStructBase = 0x16073B200; - std::uintptr_t playerStructSize = 0x4A4C0; - std::uintptr_t persistenceVar = 0x5BC; - - std::uintptr_t targetPlayerStruct = playerStructBase + client * playerStructSize; - - *(char*)(targetPlayerStruct + persistenceVar) = (char)0x5; - - if (!isPersistenceVarSet[client]) - { - printf("\n"); - printf("##################################################\n"); - printf("] SETTING PERSISTENCE VAR FOR CLIENT #%d\n", client); - printf("##################################################\n"); - printf("\n"); - isPersistenceVarSet[client] = true; - } - - return originalIsPersistenceDataAvailable(thisptr, client); -} \ No newline at end of file diff --git a/r5dedicated/dllmain.cpp b/r5dedicated/dllmain.cpp deleted file mode 100644 index 4dc7cc24..00000000 --- a/r5dedicated/dllmain.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "pch.h" -#include "dllmain.h" -#include "hooks.h" -#include "console.h" - -//############################################################################# -// INITIALIZATION -//############################################################################# - -void InitializeR5Dedicated() -{ - SetupConsole(); - Hooks::InstallHooks(); - Hooks::DedicatedPatch(); - printf("+-----------------------------------------------------------------------------+\n"); - printf("| R5 DEDICATED SERVER --------------------------------------------------- |\n"); - printf("+-----------------------------------------------------------------------------+\n"); - printf("\n"); -} - -void TerminateR5Dedicated() -{ - FreeConsole(); - Hooks::RemoveHooks(); -} - -//############################################################################# -// ENTRYPOINT -//############################################################################# - -BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) -{ - - switch (dwReason) - { - case DLL_PROCESS_ATTACH: - { - InitializeR5Dedicated(); - break; - } - - case DLL_PROCESS_DETACH: - { - TerminateR5Dedicated(); - break; - } - } - - return TRUE; -} diff --git a/r5dedicated/dllmain.h b/r5dedicated/dllmain.h deleted file mode 100644 index 7ec14cc2..00000000 --- a/r5dedicated/dllmain.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -__declspec(dllexport) void DummyExport() -{ - // Required for detours. -} \ No newline at end of file diff --git a/r5dedicated/enums.h b/r5dedicated/enums.h deleted file mode 100644 index f3ff58b5..00000000 --- a/r5dedicated/enums.h +++ /dev/null @@ -1,422 +0,0 @@ -#pragma once - -/* Enumerations */ -enum class D3D11DeviceVTbl : short -{ - // IUnknown - QueryInterface = 0, - AddRef = 1, - Release = 2, - - // ID3D11Device - CreateBuffer = 3, - CreateTexture1D = 4, - CreateTexture2D = 5, - CreateTexture3D = 6, - CreateShaderResourceView = 7, - CreateUnorderedAccessView = 8, - CreateRenderTargetView = 9, - CreateDepthStencilView = 10, - CreateInputLayout = 11, - CreateVertexShader = 12, - CreateGeometryShader = 13, - CreateGeometryShaderWithStreamOutput = 14, - CreatePixelShader = 15, - CreateHullShader = 16, - CreateDomainShader = 17, - CreateComputeShader = 18, - CreateClassLinkage = 19, - CreateBlendState = 20, - CreateDepthStencilState = 21, - CreateRasterizerState = 22, - CreateSamplerState = 23, - CreateQuery = 24, - CreatePredicate = 25, - CreateCounter = 26, - CreateDeferredContext = 27, - OpenSharedResource = 28, - CheckFormatSupport = 29, - CheckMultisampleQualityLevels = 30, - CheckCounterInfo = 31, - CheckCounter = 32, - CheckFeatureSupport = 33, - GetPrivateData = 34, - SetPrivateData = 35, - SetPrivateDataInterface = 36, - GetFeatureLevel = 37, - GetCreationFlags = 38, - GetDeviceRemovedReason = 39, - GetImmediateContext = 40, - SetExceptionMode = 41, - GetExceptionMode = 42, -}; - -enum class DXGISwapChainVTbl : short -{ - // IUnknown - QueryInterface = 0, - AddRef = 1, - Release = 2, - - // IDXGIObject - SetPrivateData = 3, - SetPrivateDataInterface = 4, - GetPrivateData = 5, - GetParent = 6, - - // IDXGIDeviceSubObject - GetDevice = 7, - - // IDXGISwapChain - Present = 8, - GetBuffer = 9, - SetFullscreenState = 10, - GetFullscreenState = 11, - GetDesc = 12, - ResizeBuffers = 13, - ResizeTarget = 14, - GetContainingOutput = 15, - GetFrameStatistics = 16, - GetLastPresentCount = 17, -}; - -#define MAX_SPLITSCREEN_CLIENT_BITS 2 -#define MAX_SPLITSCREEN_CLIENTS ( 1 << MAX_SPLITSCREEN_CLIENT_BITS ) // 4 - -enum -{ - MAX_JOYSTICKS = MAX_SPLITSCREEN_CLIENTS, - MOUSE_BUTTON_COUNT = 5, -}; - -enum JoystickAxis_t -{ - JOY_AXIS_X = 0, - JOY_AXIS_Y, - JOY_AXIS_Z, - JOY_AXIS_R, - JOY_AXIS_U, - JOY_AXIS_V, - MAX_JOYSTICK_AXES, -}; - -enum -{ - JOYSTICK_MAX_BUTTON_COUNT = 32, - JOYSTICK_POV_BUTTON_COUNT = 4, - JOYSTICK_AXIS_BUTTON_COUNT = MAX_JOYSTICK_AXES * 2, -}; - -#define JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_BUTTON + ((_joystick) * JOYSTICK_MAX_BUTTON_COUNT) + (_button) ) -#define JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_POV_BUTTON + ((_joystick) * JOYSTICK_POV_BUTTON_COUNT) + (_button) ) -#define JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_AXIS_BUTTON + ((_joystick) * JOYSTICK_AXIS_BUTTON_COUNT) + (_button) ) - -#define JOYSTICK_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ) -#define JOYSTICK_POV_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ) -#define JOYSTICK_AXIS_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ) - -enum ButtonCode_t -{ - BUTTON_CODE_INVALID = -1, - BUTTON_CODE_NONE = 0, - - KEY_FIRST = 0, - - KEY_NONE = KEY_FIRST, - KEY_0, - KEY_1, - KEY_2, - KEY_3, - KEY_4, - KEY_5, - KEY_6, - KEY_7, - KEY_8, - KEY_9, - KEY_A, - KEY_B, - KEY_C, - KEY_D, - KEY_E, - KEY_F, - KEY_G, - KEY_H, - KEY_I, - KEY_J, - KEY_K, - KEY_L, - KEY_M, - KEY_N, - KEY_O, - KEY_P, - KEY_Q, - KEY_R, - KEY_S, - KEY_T, - KEY_U, - KEY_V, - KEY_W, - KEY_X, - KEY_Y, - KEY_Z, - KEY_PAD_0, - KEY_PAD_1, - KEY_PAD_2, - KEY_PAD_3, - KEY_PAD_4, - KEY_PAD_5, - KEY_PAD_6, - KEY_PAD_7, - KEY_PAD_8, - KEY_PAD_9, - KEY_PAD_DIVIDE, - KEY_PAD_MULTIPLY, - KEY_PAD_MINUS, - KEY_PAD_PLUS, - KEY_PAD_ENTER, - KEY_PAD_DECIMAL, - KEY_LBRACKET, - KEY_RBRACKET, - KEY_SEMICOLON, - KEY_APOSTROPHE, - KEY_BACKQUOTE, - KEY_COMMA, - KEY_PERIOD, - KEY_SLASH, - KEY_BACKSLASH, - KEY_MINUS, - KEY_EQUAL, - KEY_ENTER, - KEY_SPACE, - KEY_BACKSPACE, - KEY_TAB, - KEY_CAPSLOCK, - KEY_NUMLOCK, - KEY_ESCAPE, - KEY_SCROLLLOCK, - KEY_INSERT, - KEY_DELETE, - KEY_HOME, - KEY_END, - KEY_PAGEUP, - KEY_PAGEDOWN, - KEY_BREAK, - KEY_LSHIFT, - KEY_RSHIFT, - KEY_LALT, - KEY_RALT, - KEY_LCONTROL, - KEY_RCONTROL, - KEY_LWIN, - KEY_RWIN, - KEY_APP, - KEY_UP, - KEY_LEFT, - KEY_DOWN, - KEY_RIGHT, - KEY_F1, - KEY_F2, - KEY_F3, - KEY_F4, - KEY_F5, - KEY_F6, - KEY_F7, - KEY_F8, - KEY_F9, - KEY_F10, - KEY_F11, - KEY_F12, - KEY_CAPSLOCKTOGGLE, - KEY_NUMLOCKTOGGLE, - KEY_SCROLLLOCKTOGGLE, - - KEY_LAST = KEY_SCROLLLOCKTOGGLE, - KEY_COUNT = KEY_LAST - KEY_FIRST + 1, - - // Mouse - MOUSE_FIRST = KEY_LAST + 1, - - MOUSE_LEFT = MOUSE_FIRST, - MOUSE_RIGHT, - MOUSE_MIDDLE, - MOUSE_4, - MOUSE_5, - MOUSE_WHEEL_UP, // A fake button which is 'pressed' and 'released' when the wheel is moved up - MOUSE_WHEEL_DOWN, // A fake button which is 'pressed' and 'released' when the wheel is moved down - - MOUSE_LAST = MOUSE_WHEEL_DOWN, - MOUSE_COUNT = MOUSE_LAST - MOUSE_FIRST + 1, - - // Joystick - JOYSTICK_FIRST = MOUSE_LAST + 1, - - JOYSTICK_FIRST_BUTTON = JOYSTICK_FIRST, - JOYSTICK_LAST_BUTTON = JOYSTICK_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_MAX_BUTTON_COUNT - 1), - JOYSTICK_FIRST_POV_BUTTON, - JOYSTICK_LAST_POV_BUTTON = JOYSTICK_POV_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_POV_BUTTON_COUNT - 1), - JOYSTICK_FIRST_AXIS_BUTTON, - JOYSTICK_LAST_AXIS_BUTTON = JOYSTICK_AXIS_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_AXIS_BUTTON_COUNT - 1), - - JOYSTICK_LAST = JOYSTICK_LAST_AXIS_BUTTON, - - BUTTON_CODE_LAST, - BUTTON_CODE_COUNT = BUTTON_CODE_LAST - KEY_FIRST + 1, - - // Helpers for XBox 360 - KEY_XBUTTON_UP = JOYSTICK_FIRST_POV_BUTTON, // POV buttons - KEY_XBUTTON_RIGHT, - KEY_XBUTTON_DOWN, - KEY_XBUTTON_LEFT, - - KEY_XBUTTON_A = JOYSTICK_FIRST_BUTTON, // Buttons - KEY_XBUTTON_B, - KEY_XBUTTON_X, - KEY_XBUTTON_Y, - KEY_XBUTTON_LEFT_SHOULDER, - KEY_XBUTTON_RIGHT_SHOULDER, - KEY_XBUTTON_BACK, - KEY_XBUTTON_START, - KEY_XBUTTON_STICK1, - KEY_XBUTTON_STICK2, - KEY_XBUTTON_INACTIVE_START, - - KEY_XSTICK1_RIGHT = JOYSTICK_FIRST_AXIS_BUTTON, // XAXIS POSITIVE - KEY_XSTICK1_LEFT, // XAXIS NEGATIVE - KEY_XSTICK1_DOWN, // YAXIS POSITIVE - KEY_XSTICK1_UP, // YAXIS NEGATIVE - KEY_XBUTTON_LTRIGGER, // ZAXIS POSITIVE - KEY_XBUTTON_RTRIGGER, // ZAXIS NEGATIVE - KEY_XSTICK2_RIGHT, // UAXIS POSITIVE - KEY_XSTICK2_LEFT, // UAXIS NEGATIVE - KEY_XSTICK2_DOWN, // VAXIS POSITIVE - KEY_XSTICK2_UP, // VAXIS NEGATIVE -}; - -// Buttons are not confirmed to be the same. They have been always the same throughout the source engine. Lets hope they did not change them. - -enum KeyValuesTypes -{ - TYPE_NONE = 0x0, - TYPE_STRING = 0x1, - TYPE_INT = 0x2, - TYPE_FLOAT = 0x3, - TYPE_PTR = 0x4, - TYPE_WSTRING = 0x5, - TYPE_COLOR = 0x6, - TYPE_UINT64 = 0x7, - TYPE_COMPILED_INT_BYTE = 0x8, - TYPE_COMPILED_INT_0 = 0x9, - TYPE_COMPILED_INT_1 = 0xA, - TYPE_NUMTYPES = 0xB, -}; - -enum ClientFrameStage_t -{ - FRAME_UNDEFINED = -1, // (haven't run any frames yet) - FRAME_START, - - // A network packet is being recieved - FRAME_NET_UPDATE_START, - // Data has been received and we're going to start calling PostDataUpdate - FRAME_NET_UPDATE_POSTDATAUPDATE_START, - // Data has been received and we've called PostDataUpdate on all data recipients - FRAME_NET_UPDATE_POSTDATAUPDATE_END, - // We've received all packets, we can now do interpolation, prediction, etc.. - FRAME_NET_UPDATE_END, - - // We're about to start rendering the scene - FRAME_RENDER_START, - // We've finished rendering the scene. - FRAME_RENDER_END, - - FRAME_NET_FULL_FRAME_UPDATE_ON_REMOVE -}; - -enum HostStates_t -{ - HS_NEW_GAME = 0x0, - HS_LOAD_GAME = 0x1, - HS_CHANGE_LEVEL_SP = 0x2, - HS_CHANGE_LEVEL_MP = 0x3, - HS_RUN = 0x4, - HS_GAME_SHUTDOWN = 0x5, - HS_SHUTDOWN = 0x6, - HS_RESTART = 0x7, -}; - -enum SIGNONSTATE -{ - SIGNONSTATE_NONE = 0, // no state yet; about to connect - SIGNONSTATE_CHALLENGE = 1, // client challenging server; all OOB packets - SIGNONSTATE_CONNECTED = 2, // client is connected to server; netchans ready - SIGNONSTATE_NEW = 3, // just got serverinfo and string tables - SIGNONSTATE_PRESPAWN = 4, // received signon buffers - SIGNONSTATE_GETTING_DATA = 5, // getting persistence data I assume? - SIGNONSTATE_SPAWN = 6, // ready to receive entity packets - SIGNONSTATE_FIRST_SNAP = 7, // ??? - SIGNONSTATE_FULL = 8, // we are fully connected; first non-delta packet received - SIGNONSTATE_CHANGELEVEL = 9, // server is changing level; please wait -}; - -enum FileWarningLevel_t -{ - FILESYSTEM_WARNING = -1, - FILESYSTEM_WARNING_QUIET = 0, - FILESYSTEM_WARNING_REPORTUNCLOSED, - FILESYSTEM_WARNING_REPORTUSAGE, - FILESYSTEM_WARNING_REPORTALLACCESSES, - FILESYSTEM_WARNING_REPORTALLACCESSES_READ, - FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE, - FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC -}; - -#define FCVAR_NONE 0 - -// Command to ConVars and ConCommands -// ConVar Systems -#define FCVAR_UNREGISTERED (1<<0) // If this is set, don't add to linked list, etc. -#define FCVAR_DEVELOPMENTONLY (1<<1) // Hidden in released products. Flag is removed automatically if ALLOW_DEVELOPMENT_CVARS is defined. -#define FCVAR_GAMEDLL (1<<2) // defined by the game DLL -#define FCVAR_CLIENTDLL (1<<3) // defined by the client DLL -#define FCVAR_HIDDEN (1<<4) // Hidden. Doesn't appear in find or auto complete. Like DEVELOPMENTONLY, but can't be compiled out. - -// ConVar only -#define FCVAR_PROTECTED (1<<5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value -#define FCVAR_SPONLY (1<<6) // This cvar cannot be changed by clients connected to a multiplayer server. -#define FCVAR_ARCHIVE (1<<7) // set to cause it to be saved to vars.rc -#define FCVAR_NOTIFY (1<<8) // notifies players when changed -#define FCVAR_USERINFO (1<<9) // changes the client's info string - -#define FCVAR_PRINTABLEONLY (1<<10) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). - -#define FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS (1<<10) // When on concommands this allows remote clients to execute this cmd on the server. - // We are changing the default behavior of concommands to disallow execution by remote clients without - // this flag due to the number existing concommands that can lag or crash the server when clients abuse them. - -#define FCVAR_UNLOGGED (1<<11) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log -#define FCVAR_NEVER_AS_STRING (1<<12) // never try to print that cvar - -// It's a ConVar that's shared between the client and the server. -// At signon, the values of all such ConVars are sent from the server to the client (skipped for local -// client, of course ) -// If a change is requested it must come from the console (i.e., no remote client changes) -// If a value is changed while a server is active, it's replicated to all connected clients -#define FCVAR_REPLICATED (1<<13) // server setting enforced on clients, TODO rename to FCAR_SERVER at some time -#define FCVAR_CHEAT (1<<14) // Only useable in singleplayer / debug / multiplayer & sv_cheats -#define FCVAR_SS (1<<15) // causes varnameN where N == 2 through max splitscreen slots for mod to be autogenerated -#define FCVAR_DEMO (1<<16) // record this cvar when starting a demo file -#define FCVAR_DONTRECORD (1<<17) // don't record these command in demofiles -#define FCVAR_SS_ADDED (1<<18) // This is one of the "added" FCVAR_SS variables for the splitscreen players -#define FCVAR_RELEASE (1<<19) // Cvars tagged with this are the only cvars avaliable to customers -#define FCVAR_RELOAD_MATERIALS (1<<20) // If this cvar changes, it forces a material reload -#define FCVAR_RELOAD_TEXTURES (1<<21) // If this cvar changes, if forces a texture reload - -#define FCVAR_NOT_CONNECTED (1<<22) // cvar cannot be changed by a client that is connected to a server -#define FCVAR_MATERIAL_SYSTEM_THREAD (1<<23) // Indicates this cvar is read from the material system thread -#define FCVAR_ARCHIVE_GAMECONSOLE (1<<24) // cvar written to config.cfg on the Xbox - -#define FCVAR_SERVER_CAN_EXECUTE (1<<28)// the server is allowed to execute this command on clients via ClientCommand/NET_StringCmd/CBaseClientState::ProcessStringCmd. -#define FCVAR_SERVER_CANNOT_QUERY (1<<29)// If this is set, then the server is not allowed to query this cvar's value (via IServerPluginHelpers::StartQueryCvarValue). -#define FCVAR_CLIENTCMD_CAN_EXECUTE (1<<30) // IVEngineClient::ClientCmd is allowed to execute this command. - -#define MAX_PLAYERS 128 // Max R5 players. diff --git a/r5dedicated/gameclasses.cpp b/r5dedicated/gameclasses.cpp deleted file mode 100644 index 9f9cf1e4..00000000 --- a/r5dedicated/gameclasses.cpp +++ /dev/null @@ -1,470 +0,0 @@ -#include "pch.h" -#include "enums.h" -#include "gameclasses.h" - -// Need this for a re-factor later. -// Interface* interfaces = *reinterpret_cast(0x167F4FA48); - -// for (Interface* current = interfaces; current; current = reinterpret_cast(current->NextInterfacePtr)) -// { -// printf("%s: %p\n", current->InterfaceName, current->InterfacePtr); -// } - -namespace GameGlobals -{ - bool IsInitialized = false; - CHostState* HostState = nullptr; - CInputSystem* InputSystem = nullptr; - CCVar* Cvar = nullptr; - CClient* Client = nullptr; - BanList* BanSystem = new BanList(); - - CKeyValuesSystem* KeyValuesSystem = nullptr; - KeyValues** PlaylistKeyValues = nullptr; - - std::vector allPlaylists = { "none" }; - - namespace CustomCommandVariations - { - - void Kick_Callback(CCommand* cmd) - { - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - const char* firstArg = cmdReference[1]; // Get first arg. - - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. - std::string clientName((char*)clientNamePtr, 32); // Get full name. - - if (clientName.empty()) // Empty name? - continue; - - if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? - continue; - - DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. - } - } - - void KickID_Callback(CCommand* cmd) - { - static auto HasOnlyDigits = [](const std::string& string) - { - for (const char& character : string) - { - if (std::isdigit(character) == 0) - return false; - } - return true; - }; - - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - std::string firstArg = cmdReference[1]; // Get first arg. - - try - { - bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. - MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. - if (ipAddressField) - { - std::stringstream ss; - ss << std::to_string(ipAddressField.GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x3).GetValue()); - - finalIPAddress = ss.str(); - } - - if (onlyDigits) - { - std::int64_t ID = static_cast(std::stoll(firstArg)); - if (ID > MAX_PLAYERS) // Is it a possible originID? - { - std::int64_t originID = client->m_iOriginID; - if (originID != ID) // See if they match. - continue; - } - else // If its not try by userID. - { - std::int64_t clientID = static_cast(client->m_iUserID + 1); // Get UserID + 1. - if (clientID != ID) // See if they match. - continue; - } - - DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. - } - else - { - if (firstArg.compare(finalIPAddress) != NULL) // Do the string equal? - continue; - - DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. - } - } - } - catch (std::exception& e) - { - std::cout << "Kick UID asked for a userID or originID :( You can get the userID with the 'status' command. Error: " << e.what() << std::endl; - return; - } - } - - void Unban_Callback(CCommand* cmd) - { - static auto HasOnlyDigits = [](const std::string& string) - { - for (const char& character : string) - { - if (std::isdigit(character) == 0) - return false; - } - return true; - }; - - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - - try - { - const char* firstArg = cmdReference[1]; - if (HasOnlyDigits(firstArg)) // Check if we have an ip address or origin ID. - { - GameGlobals::BanSystem->DeleteEntry("noIP", std::stoll(firstArg)); // Delete ban entry. - GameGlobals::BanSystem->Save(); // Save modified vector to file. - } - else - { - GameGlobals::BanSystem->DeleteEntry(firstArg, 1); // Delete ban entry. - GameGlobals::BanSystem->Save(); // Save modified vector to file. - } - } - catch (std::exception& e) - { - std::cout << "Unban Error: " << e.what() << std::endl; - return; - } - } - - void ReloadBanList_Callback(CCommand* cmd) - { - GameGlobals::BanSystem->Load(); // Reload banlist. - } - - void Ban_Callback(CCommand* cmd) - { - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - const char* firstArg = cmdReference[1]; // Get first arg. - - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. - std::string clientName((char*)clientNamePtr, 32); // Get full name. - - if (clientName.empty()) // Empty name? - continue; - - if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? - continue; - - std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. - MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. - if (ipAddressField && ipAddressField.GetValue() != 0x0) - { - std::stringstream ss; - ss << std::to_string(ipAddressField.GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x3).GetValue()); - - finalIPAddress = ss.str(); - } - - GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. - GameGlobals::BanSystem->Save(); // Save ban list. - DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. - } - } - - void BanID_Callback(CCommand* cmd) - { - static auto HasOnlyDigits = [](const std::string& string) - { - for (const char& character : string) - { - if (std::isdigit(character) == 0) - return false; - } - return true; - }; - - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - std::string firstArg = cmdReference[1]; - - try - { - bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. - MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. - if (ipAddressField) - { - std::stringstream ss; - ss << std::to_string(ipAddressField.GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x3).GetValue()); - - finalIPAddress = ss.str(); - } - - if (onlyDigits) - { - std::int64_t ID = static_cast(std::stoll(firstArg)); - if (ID > MAX_PLAYERS) // Is it a possible originID? - { - std::int64_t originID = client->m_iOriginID; - if (originID != ID) // See if they match. - continue; - } - else // If its not try by userID. - { - std::int64_t clientID = static_cast(client->m_iUserID + 1); // Get UserID + 1. - if (clientID != ID) // See if they match. - continue; - } - - GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. - GameGlobals::BanSystem->Save(); // Save ban list. - DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. - } - else - { - if (firstArg.compare(finalIPAddress) != NULL) // Do the string equal? - continue; - - GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. - GameGlobals::BanSystem->Save(); // Save ban list. - DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. - } - } - } - catch (std::exception& e) - { - std::cout << "Banid Error: " << e.what() << std::endl; - return; - } - } - } - - void NullHostNames() - { - const char* hostnameArray[] = - { - "pin_telemetry_hostname", - "assetdownloads_hostname", - "users_hostname", - "persistence_hostname", - "speechtotexttoken_hostname", - "communities_hostname", - "persistenceDef_hostname", - "party_hostname", - "speechtotext_hostname", - "serverReports_hostname", - "subscription_hostname", - "steamlink_hostname", - "staticfile_hostname", - "matchmaking_hostname", - "skill_hostname", - "publication_hostname", - "stats_hostname" - }; - - for (int i = 0; i < 17; i++) - { - const char* name = hostnameArray[i]; - Cvar->FindVar(name)->m_pzsCurrentValue = "0.0.0.0"; - } - } - - void InitGameGlobals() - { - HostState = reinterpret_cast(0x141736120); // Get CHostState from memory. - InputSystem = *reinterpret_cast(0x14D40B380); // Get IInputSystem from memory. - Cvar = *reinterpret_cast(0x14D40B348); // Get CCVar from memory. - //KeyValuesSystem = reinterpret_cast(0x141F105C0); // Get CKeyValuesSystem from memory. - //PlaylistKeyValues = reinterpret_cast(0x16705B980); // Get the KeyValue for the playlist file. - //Client = reinterpret_cast(0x16073B200); - - //NullHostNames(); // Null all hostnames. - //InitAllCommandVariations(); // Initialize our custom ConVars. - //*(char*)addr_m_bRestrictServerCommands = true; // Restrict commands. - //void* disconnect = Cvar->FindCommand("disconnect"); - //*(std::int32_t*)((std::uintptr_t)disconnect + 0x38) |= FCVAR_SERVER_CAN_EXECUTE; // Make sure server is not restricted to this. - - //std::thread t1(InitPlaylist); // Start thread to grab playlists. - //t1.detach(); // Detach thread from current one. - - IsInitialized = true; - } - - void InitPlaylist() - { - while (true) - { - if ((*PlaylistKeyValues)) - { - KeyValues* playlists = (*PlaylistKeyValues)->FindKey("Playlists", false); // Find playlists key. - if (playlists) - { - allPlaylists.clear(); - for (KeyValues* dat = playlists->m_pSub; dat != nullptr; dat = dat->m_pPeer) // Parse through all sub keys. - { - allPlaylists.push_back(dat->GetName()); // Get all playlist names. - } - - break; // Break if playlist got filled. - } - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - } - - void InitAllCommandVariations() - { - void* KickConCommand = CreateCustomConCommand("kick", "Kick a client from the Server via name. | Usage: kick (name).", 0, CustomCommandVariations::Kick_Callback, nullptr); - void* KickIDConCommand = CreateCustomConCommand("kickid", "Kick a client from the Server via userID or originID | Usage: kickid (originID/userID)", 0, CustomCommandVariations::KickID_Callback, nullptr); - void* UnbanConCommand = CreateCustomConCommand("unban", "Unbans a client from the Server via IP or originID | Usage: unban (originID/ipAddress)", 0, CustomCommandVariations::Unban_Callback, nullptr); - void* ReloadBanListConCommand = CreateCustomConCommand("reloadbanlist", "Reloads the ban list from disk.", 0, CustomCommandVariations::ReloadBanList_Callback, nullptr); - void* BanConCommand = CreateCustomConCommand("ban", "Bans a client from the Server via name. | Usage: ban (name)", 0, CustomCommandVariations::Ban_Callback, nullptr); - void* BanIDConCommand = CreateCustomConCommand("banid", "Bans a client from the Server via originID, userID or IP | Usage: banid (originID/ipAddress/userID)", 0, CustomCommandVariations::BanID_Callback, nullptr); - } - - void* CreateCustomConCommand(const char* name, const char* helpString, int flags, void* callback, void* callbackAfterExecution) - { - static MemoryAddress ConCommandVtable = MemoryAddress(0x14136BD70); - static MemoryAddress NullSub = MemoryAddress(0x1401B3280); - static MemoryAddress CallbackCompletion = MemoryAddress(0x1401E3990); - static MemoryAddress RegisterConCommand = MemoryAddress(0x14046F470); - - void* command = reinterpret_cast(addr_MemAlloc_Wrapper(0x68)); // Allocate new memory with StdMemAlloc else we crash. - memset(command, 0, 0x68); // Set all to null. - std::uintptr_t commandPtr = reinterpret_cast(command); // To ptr. - - *(void**)commandPtr = ConCommandVtable.RCast(); // 0x0 to ConCommand vtable. - *(const char**)(commandPtr + 0x18) = name; // 0x18 to ConCommand Name. - *(const char**)(commandPtr + 0x20) = helpString; // 0x20 to ConCommand help string. - *(std::int32_t*)(commandPtr + 0x38) = flags; // 0x38 to ConCommand Flags. - *(void**)(commandPtr + 0x40) = NullSub.RCast(); // 0x40 Nullsub since every concommand has it. - *(void**)(commandPtr + 0x50) = callback; // 0x50 has function callback. - *(std::int32_t*)(commandPtr + 0x60) = 2; // 0x60 Set to use callback and newcommand callback. - - if (callbackAfterExecution) // Do we wanna have a callback after execution? - { - *(void**)(commandPtr + 0x58) = callbackAfterExecution; // 0x58 to our callback after execution. - } - else - { - *(void**)(commandPtr + 0x58) = CallbackCompletion.RCast(); // 0x58 nullsub. - } - - RegisterConCommand.RCast()((void*)commandPtr); // Register command in ConVarAccessor. - - return command; - } - - ConVar* CreateCustomConVar(const char* name, const char* defaultValue, int flags, const char* helpString, bool bMin, float fMin, bool bMax, float fMax, void* callback, void* unk) - { - static MemoryAddress ConVarVtable = MemoryAddress(0x14046FB50).Offset(0x12).ResolveRelativeAddress(); // Get vtable ptr for ConVar table. - static MemoryAddress ICvarVtable = MemoryAddress(0x14046FB50).Offset(0x29).ResolveRelativeAddress(); // Get vtable ptr for ICvar table. - static MemoryAddress CreateConVar = MemoryAddress(0x140470540); // Get CreateConvar address. - - ConVar* allocatedConvar = reinterpret_cast(addr_MemAlloc_Wrapper(0xA0)); // Allocate new memory with StdMemAlloc else we crash. - memset(allocatedConvar, 0, 0xA0); // Set all to null. - std::uintptr_t cvarPtr = reinterpret_cast(allocatedConvar); // To ptr. - - *(void**)(cvarPtr + 0x40) = ICvarVtable.RCast(); // 0x40 to ICvar table. - *(void**)cvarPtr = ConVarVtable.RCast(); // 0x0 to ConVar vtable. - - CreateConVar.RCast() // Call to create ConVar. - (allocatedConvar, name, defaultValue, flags, helpString, bMin, fMin, bMax, fMax, callback, unk); - - return allocatedConvar; // Return allocated ConVar. - } - - void DisconnectClient(CClient* client, const char* reason, unsigned __int8 unk1, char unk2) - { - if (!client) // Client valid? - return; - - if (std::strlen(reason) == NULL) // Is reason null? - return; - - if (!client->GetNetChan()) - return; - - addr_NetChan_Shutdown(client->GetNetChan(), reason, unk1, unk2); // Shutdown netchan. - client->GetNetChan() = nullptr; // Null netchan. - MemoryAddress(0x140302FD0).RCast()(client); // Reset CClient instance for client. - } -} - -#pragma region KeyValues -const char* KeyValues::GetName() -{ - return GameGlobals::KeyValuesSystem->GetStringForSymbol(MAKE_3_BYTES_FROM_1_AND_2(m_iKeyNameCaseSensitive, m_iKeyNameCaseSensitive2)); -} -#pragma endregion \ No newline at end of file diff --git a/r5dedicated/gameclasses.h b/r5dedicated/gameclasses.h deleted file mode 100644 index 39ebf687..00000000 --- a/r5dedicated/gameclasses.h +++ /dev/null @@ -1,487 +0,0 @@ -#pragma once -#include "pch.h" -#include "hooks.h" -#include "enums.h" -#include "banlist.h" - -///////////////////////////////////////////////////////////////////////////// -// Classes and Structs -class CInputSystem -{ -public: - void EnableInput(bool bEnabled)// @0x14039F100 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CInputSystem*, bool); - (*reinterpret_cast(this))[10](this, bEnabled); - } - - void EnableMessagePump(bool bEnabled) // @0x14039F110 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CInputSystem*, bool); - (*reinterpret_cast(this))[11](this, bEnabled); - } - - bool IsButtonDown(ButtonCode_t Button) // @0x1403A0140 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = bool(__thiscall*)(CInputSystem*, ButtonCode_t); - return (*reinterpret_cast(this))[13](this, Button); - } - -private: - char pad_0000[16]; //0x0000 -public: - bool m_bEnabled; //0x0010 IsInputEnabled variable. - bool m_bPumpEnabled; //0x0011 EnabledMessagePump variable. -}; - -typedef int HKeySymbol; - -#define MAKE_3_BYTES_FROM_1_AND_2( x1, x2 ) (( (( std::uint16_t )x2) << 8 ) | (std::uint8_t)(x1)) - -class CKeyValuesSystem // VTABLE @ 0x1413AA1E8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM -{ -public: - - void RegisterSizeofKeyValues(__int64 size) //@0x1413AA1F0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, __int64); - (*reinterpret_cast(this))[0](this, size); - } - - void* AllocKeyValuesMemory(__int64 size) // @0x1413AA1F8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void* (__thiscall*)(CKeyValuesSystem*, __int64); - return (*reinterpret_cast(this))[1](this, size); - } - - void FreeKeyValuesMemory(void* pMem) // @0x1413AA200 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, void*); - (*reinterpret_cast(this))[2](this, pMem); - } - - HKeySymbol GetSymbolForString(const char* name, bool bCreate) // @0x1413AA208 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = HKeySymbol(__thiscall*)(CKeyValuesSystem*, const char*, bool); - return (*reinterpret_cast(this))[3](this, name, bCreate); - } - - const char* GetStringForSymbol(HKeySymbol symbol) // @0x1413AA210 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = const char* (__thiscall*)(CKeyValuesSystem*, HKeySymbol); - return (*reinterpret_cast(this))[4](this, symbol); - } - - // void __fastcall CKeyValuesSystem::FreeKeyValuesMemory(CKeyValuesSystem* this_arg, void* ptr_mem_arg) - // { - // __int64* v2; // rax - // __int64 v4; // rax - // __int64* v5; // rax - // - // v2 = qword_14D40B538; - // if (!qword_14D40B538) - // { - // v2 = sub_140462930(); - // qword_14D40B538 = v2; - // } - // v4 = (*(*v2 + 48))(v2, ptr_mem_arg); - // if (v4 > 0) - // CKeyValuesSystem::m_pMemPool -= v4; - // v5 = qword_14D40B538; - // if (!qword_14D40B538) - // { - // v5 = sub_140462930(); - // qword_14D40B538 = v5; - // } - // (*(*v5 + 40))(v5, ptr_mem_arg); - // } - - // GetMemPool return a global variable called m_pMemPool it gets modified by AllocKeyValuesMemory and FreeKeyValuesMemory above you can see where the find it in FreeKeyValuesMemory. - void* GetMemPool() // @0x1413AA228 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - return reinterpret_cast(0x14D412768); // May need to dereference is once more not sure right now. - } - - void SetKeyValuesExpressionSymbol(const char* name, bool bValue) // @0x1413AA230 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, const char*, bool); - (*reinterpret_cast(this))[8](this, name, bValue); - } - - bool GetKeyValuesExpressionSymbol(const char* name) // @0x1413AA238 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = bool(__thiscall*)(CKeyValuesSystem*, const char*); - return (*reinterpret_cast(this))[9](this, name); - } - - HKeySymbol GetSymbolForStringCaseSensitive(HKeySymbol& hCaseInsensitiveSymbol, const char* name, bool bCreate) // @0x1413AA240 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = HKeySymbol(__thiscall*)(CKeyValuesSystem*, HKeySymbol&, const char*, bool); - return (*reinterpret_cast(this))[10](this, hCaseInsensitiveSymbol, name, bCreate); - } - - // Datatypes aren't accurate. But full fill the actual byte distance. -public: - void* vtable; // 0x0000 - __int64 m_iMaxKeyValuesSize; // 0x0008 -private: - char gap10[240]; // 0x0010 -public: - __int32 m_KvConditionalSymbolTable; // 0x0100 -private: - char gap104[4]; // 0x0104 -public: - __int64 field_108; // 0x0108 -private: - char gap110[32]; // 0x0110 -public: - int m_mutex; // 0x0130 -}; - -class KeyValues -{ -public: - - KeyValues* FindKey(const char* keyName, bool bCreate) - { - static auto func = reinterpret_cast(addr_KeyValues_FindKey); - return func(this, keyName, bCreate); - } - - const char* GetName(); - - int GetInt(const char* keyName, int defaultValue) - { - KeyValues* dat = FindKey(keyName, false); - - if (!dat) - return defaultValue; - - switch (dat->m_iDataType) - { - case TYPE_STRING: - return atoi(dat->m_sValue); - case TYPE_FLOAT: - return static_cast(m_flValue()); - case TYPE_WSTRING: - return _wtoi(dat->m_wsValue); - case TYPE_UINT64: - return 0; - default: - return dat->m_iValue(); - } - - return defaultValue; - } - - void SetInt(const char* keyName, int iValue) - { - KeyValues* dat = FindKey(keyName, true); - if (dat) - { - dat->m_iValue() = iValue; - dat->m_iDataType = TYPE_INT; - } - } - - void SetFloat(const char* keyName, float flValue) - { - KeyValues* dat = FindKey(keyName, true); - if (dat) - { - dat->m_flValue() = flValue; - dat->m_iDataType = TYPE_FLOAT; - } - } - - // Compiler makes it so m_Value shares the offset spot with m_flValue that why we cast it like this. - float& m_flValue() - { - static __int32 offset = 0x18; - return *(float*)((std::uintptr_t)this + offset); - } - - int& m_iValue() - { - static __int32 offset = 0x18; - return *(int*)((std::uintptr_t)this + offset); - } - -public: - unsigned __int32 m_iKeyName : 24; // 0x0000 - unsigned __int32 m_iKeyNameCaseSensitive : 8; // 0x0003 - char* m_sValue; // 0x0008 - wchar_t* m_wsValue; // 0x0010 - int m_Value; // 0x0018 -private: - char gap1C[12]; // 0x0020 -public: - char m_iDataType; // 0x0028 - unsigned __int16 m_iKeyNameCaseSensitive2; // 0x002A - KeyValues* m_pPeer; // 0x0030 - KeyValues* m_pSub; // 0x0038 - KeyValues* m_pChain; // 0x0040 -}; - -struct Vector3 // Implement the proper class of this at some point. -{ - float x; // 0x0000 - float y; // 0x0004 - float z; // 0x0008 -}; - -struct QAngle // Implement the proper class of this at some point. -{ - float pitch; //0x0000 - float yaw; // 0x0004 - float roll; // 0x0008 -}; - -class CHostState -{ -public: - int m_iCurrentState; //0x0000 - int m_iNextState; //0x0004 - Vector3 m_vecLocation; //0x0008 - QAngle m_angLocation; //0x0014 - char m_levelName[64]; //0x0020 - char m_mapGroupName[256]; //0x0060 - char m_landMarkName[256]; //0x0160 - float m_flShortFrameTime; //0x0260 - bool m_bActiveGame; //0x0264 - bool m_bRememberLocation; //0x0265 - bool m_bBackgroundLevel; //0x0266 - bool m_bWaitingForConnection; //0x0267 - bool m_bSplitScreenConnect; //0x0268 - bool m_bGameHasShutDownAndFlushedMemory; //0x0269 - bool m_bWorkshopMapDownloadPending; //0x026A -}; - -class CHLClient -{ -public: - void FrameStageNotify(ClientFrameStage_t curStage) // @0x1405C0740 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CHLClient*, ClientFrameStage_t); - (*reinterpret_cast(this))[58](this, curStage); /* 48 83 EC 28 89 15 ? ? ? ? */ - } -}; - -class CClient -{ -public: - inline CClient* GetClientInstance(int index) - { - return (CClient*)(std::uintptr_t)(0x16073B200 + (index * (std::uintptr_t)0x4A4C0)); - } - - void*& GetNetChan() - { - return m_nNetChannel; - } -private: - char pad_0000[16]; //0x0000 -public: - int m_iUserID; //0x0010 -private: - char pad_0014[908]; //0x0014 -public: - void* m_nNetChannel; //0x03A0 -private: - char pad_03A8[8]; //0x03A8 -public: - int m_iSignonstate; //0x03B0 -private: - char pad_03B4[4]; //0x03B4 -public: - std::int64_t m_iOriginID; //0x03B8 -private: - char pad_03C0[303360]; //0x03C0 -}; - -class CCommand -{ -private: - enum - { - COMMAND_MAX_ARGC = 64, - COMMAND_MAX_LENGTH = 512, - }; - -public: - CCommand() = delete; - - inline int MaxCommandLength() - { - return COMMAND_MAX_LENGTH - 1; - } - - inline int64_t ArgC() const - { - return m_nArgc; - } - - inline const char** ArgV() const - { - return m_nArgc ? (const char**)m_ppArgv : NULL; - } - - inline const char* ArgS() const - { - return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : ""; - } - - inline const char* GetCommandString() const - { - return m_nArgc ? m_pArgSBuffer : ""; - } - - inline const char* Arg(int nIndex) const - { - // FIXME: Many command handlers appear to not be particularly careful - // about checking for valid argc range. For now, we're going to - // do the extra check and return an empty string if it's out of range - if (nIndex < 0 || nIndex >= m_nArgc) - return ""; - return m_ppArgv[nIndex]; - } - - inline const char* operator[](int nIndex) const - { - return Arg(nIndex); - } - -private: - std::int64_t m_nArgc; - std::int64_t m_nArgv0Size; - char m_pArgSBuffer[COMMAND_MAX_LENGTH]; - char m_pArgvBuffer[COMMAND_MAX_LENGTH]; - const char* m_ppArgv[COMMAND_MAX_ARGC]; -}; - -class ConCommandBase -{ -public: - void* m_pConCommandBaseVTable; //0x0000 - ConCommandBase* m_pNext; //0x0008 - bool m_bRegistered; //0x0010 -private: - char pad_0011[7]; //0x0011 -public: - const char* m_pszName; //0x0018 - const char* m_pszHelpString; //0x0020 -private: - char pad_0028[16]; //0x0028 -public: - __int32 m_nFlags; //0x0038 -private: - char pad_003C[4]; //0x003C -}; //Size: 0x0038 - -class ConVar -{ -public: - ConCommandBase m_ConCommandBase; // 0x0000 - void* m_pConVarVTable; //0x0040 - ConVar* m_pParent; //0x0048 - const char* n_pszDefaultValue; //0x0050 - const char* m_pzsCurrentValue; //0x0058 - __int64 m_iStringLength; //0x0060 - float m_flValue; //0x0068 - int m_iValue; //0x006C - bool m_bHasMin; //0x0070 - float m_flMinValue; //0x0074 - bool m_bHasMax; //0x0078 - float m_flMaxValue; //0x007C - char pad_0080[32]; //0x0080 -}; //Size: 0x00A0 - -class CCVarIteratorInternal // Fully reversed table, just look at the virtual function table and rename the function. -{ -public: - virtual void SetFirst(void) = 0; //0 - virtual void Next(void) = 0; //1 - virtual bool IsValid(void) = 0; //2 - virtual ConCommandBase* Get(void) = 0; //3 -}; - -class CCVar -{ -public: - ConCommandBase* FindCommandBase(const char* szCommandName) // @0x1405983A0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = ConCommandBase*(__thiscall*)(CCVar*, const char*); - return (*reinterpret_cast(this))[14](this, szCommandName); - } - - ConVar* FindVar(const char* szVarName) // @0x1405983B0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = ConVar*(__thiscall*)(CCVar*, const char*); - return (*reinterpret_cast(this))[16](this, szVarName); - } - - void* /*Implement ConCommand class.*/ FindCommand(const char* szCommandName) // @0x1405983F0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void*(__thiscall*)(CCVar*, const char*); - return (*reinterpret_cast(this))[18](this, szCommandName); - } - - CCVarIteratorInternal* FactoryInternalIterator() // @0x140597C10 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = CCVarIteratorInternal*(__thiscall*)(CCVar*); - return (*reinterpret_cast(this))[41](this); - } - - std::unordered_map DumpToMap() - { - std::stringstream ss; - CCVarIteratorInternal* itint = FactoryInternalIterator(); // Allocatd new InternalIterator. - - std::unordered_map allConVars; - - for (itint->SetFirst(); itint->IsValid(); itint->Next()) // Loop through all instances. - { - ConCommandBase* command = itint->Get(); - const char* commandName = command->m_pszName; - allConVars[commandName] = command; - } - - return allConVars; - } -}; - -struct Interface -{ - __int64 (*InterfacePtr)(void); - const char* InterfaceName; - __int64* NextInterfacePtr; -}; - -///////////////////////////////////////////////////////////////////////////// -// Initialize Game Globals -namespace GameGlobals -{ - // Class Instances - extern CHostState* HostState; - extern CInputSystem* InputSystem; - extern CCVar* Cvar; - extern KeyValues** PlaylistKeyValues; - extern CKeyValuesSystem* KeyValuesSystem; - extern CClient* Client; - extern BanList* BanSystem; - - // Var - ConVar* CreateCustomConVar(const char* name, const char* defaultValue, int flags, const char* helpString, bool bMin, float fMin, bool bMax, float fMax, void* callback, void* unk); - void* CreateCustomConCommand(const char* name, const char* helpString, int flags, void* callback, void* callbackAfterExecution); - - // Init - void InitGameGlobals(); - void InitAllCommandVariations(); - void InitPlaylist(); - - extern std::vector allPlaylists; - extern bool IsInitialized; - - // Utility - void DisconnectClient(CClient* client, const char* reason, unsigned __int8 unk1, char unk2); -} \ No newline at end of file diff --git a/r5dedicated/hooks.cpp b/r5dedicated/hooks.cpp deleted file mode 100644 index ad488c56..00000000 --- a/r5dedicated/hooks.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "opcodes.h" -#include "gameclasses.h" - -void Hooks::InstallHooks() -{ - /////////////////////////////////////////////////////////////////////////////// - // Initialize Minhook - MH_Initialize(); - - /////////////////////////////////////////////////////////////////////////////// - // Hook SourceAppSystemGroup functions - MH_CreateHook(addr_CSourceAppSystemGroup_Create, &Hooks::CSourceAppSystemGroup_Create, reinterpret_cast(&originalCSourceAppSystemGroup_Create)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Squirrel functions - MH_CreateHook(addr_SQVM_Print, &Hooks::SQVM_Print, NULL); - MH_CreateHook(addr_SQVM_LoadRson, &Hooks::SQVM_LoadRson, reinterpret_cast(&originalSQVM_LoadRson)); - MH_CreateHook(addr_SQVM_LoadScript, &Hooks::SQVM_LoadScript, reinterpret_cast(&originalSQVM_LoadScript)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Game Functions - // MH_CreateHook(addr_CHLClient_FrameStageNotify, &Hooks::FrameStageNotify, reinterpret_cast(&originalFrameStageNotify)); - MH_CreateHook(addr_CVEngineServer_IsPersistenceDataAvailable, &Hooks::IsPersistenceDataAvailable, reinterpret_cast(&originalIsPersistenceDataAvailable)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Netchan functions - MH_CreateHook(addr_NET_ReceiveDatagram, &Hooks::NET_ReceiveDatagram, reinterpret_cast(&originalNET_ReceiveDatagram)); - MH_CreateHook(addr_NET_SendDatagram, &Hooks::NET_SendDatagram, reinterpret_cast(&originalNET_SendDatagram)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook ConVar | ConCommand functions. - MH_CreateHook(addr_ConVar_IsFlagSet, &Hooks::ConVar_IsFlagSet, NULL); - MH_CreateHook(addr_ConCommand_IsFlagSet, &Hooks::ConCommand_IsFlagSet, NULL); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Utility functions - MH_CreateHook(addr_MSG_EngineError, &Hooks::MSG_EngineError, reinterpret_cast(&originalMSG_EngineError)); - - /////////////////////////////////////////////////////////////////////////////// - // Enable SourceAppSystemGroup hooks - MH_EnableHook(addr_CSourceAppSystemGroup_Create); - - /////////////////////////////////////////////////////////////////////////////// - // Enable Squirrel hooks - MH_EnableHook(addr_SQVM_Print); - MH_EnableHook(addr_SQVM_LoadRson); - MH_EnableHook(addr_SQVM_LoadScript); - - /////////////////////////////////////////////////////////////////////////////// - // Enable Game hooks - MH_EnableHook(addr_CHLClient_FrameStageNotify); - MH_EnableHook(addr_CVEngineServer_IsPersistenceDataAvailable); - - /////////////////////////////////////////////////////////////////////////////// - // Enable ConVar | ConCommand hooks - MH_EnableHook(addr_ConVar_IsFlagSet); - MH_EnableHook(addr_ConCommand_IsFlagSet); - - /////////////////////////////////////////////////////////////////////////////// - // Enabled Utility hooks - MH_EnableHook(addr_MSG_EngineError); - - /////////////////////////////////////////////////////////////////////////////// - // Set global variables - GameGlobals::InitGameGlobals(); -} - -void Hooks::RemoveHooks() -{ - /////////////////////////////////////////////////////////////////////////////// - // Hook SourceAppSystemGroup functions - MH_RemoveHook(addr_CSourceAppSystemGroup_Create); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Squirrel functions - MH_RemoveHook(addr_SQVM_Print); - MH_RemoveHook(addr_SQVM_LoadRson); - MH_RemoveHook(addr_SQVM_LoadScript); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Game Functions - MH_RemoveHook(addr_CHLClient_FrameStageNotify); - MH_RemoveHook(addr_CVEngineServer_IsPersistenceDataAvailable); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Netchan functions - MH_RemoveHook(addr_NET_ReceiveDatagram); - MH_RemoveHook(addr_NET_SendDatagram); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook ConVar | ConCommand functions. - MH_RemoveHook(addr_ConVar_IsFlagSet); - MH_RemoveHook(addr_ConCommand_IsFlagSet); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Utility functions - MH_RemoveHook(addr_MSG_EngineError); - - /////////////////////////////////////////////////////////////////////////////// - // Reset Minhook - MH_Uninitialize(); -} - -//################################################################################# -// TOGGLES -//################################################################################# - -void Hooks::ToggleDevCommands() -{ - static bool bDev = true;; - - if (!bDev) - { - MH_EnableHook(addr_ConVar_IsFlagSet); - MH_EnableHook(addr_ConCommand_IsFlagSet); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>>| DEVONLY COMMANDS ACTIVATED |<<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); - - } - else - { - MH_DisableHook(addr_ConVar_IsFlagSet); - MH_DisableHook(addr_ConCommand_IsFlagSet); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>| DEVONLY COMMANDS DEACTIVATED |<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); - } - - bDev = !bDev; -} - -void Hooks::ToggleNetTrace() -{ - static bool bNet = true; - - if (!bNet) - { - MH_EnableHook(addr_NET_ReceiveDatagram); - MH_EnableHook(addr_NET_SendDatagram); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>>| NETCHANNEL TRACE ACTIVATED |<<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); - } - else - { - MH_DisableHook(addr_NET_ReceiveDatagram); - MH_DisableHook(addr_NET_SendDatagram); - printf("\n"); - printf("+--------------------------------------------------------+\n"); - printf("|>>>>>>>>>>>>| NETCHANNEL TRACE DEACTIVATED |<<<<<<<<<<<<|\n"); - printf("+--------------------------------------------------------+\n"); - printf("\n"); - } - - bNet = !bNet; -} diff --git a/r5dedicated/hooks.h b/r5dedicated/hooks.h deleted file mode 100644 index 360bddc7..00000000 --- a/r5dedicated/hooks.h +++ /dev/null @@ -1,169 +0,0 @@ -#pragma once -#include "pch.h" -// Define the signatures or offsets to be searched and hooked -namespace -{ - Module r5_patterns = Module("r5apex.exe"); // Create module class instance. - -#pragma region CSourceAppSystemGroup - FUNC_AT_ADDRESS(addr_CSourceAppSystemGroup_Create, char(*)(__int64), r5_patterns.PatternSearch("48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 48 8B F9 E8 ? ? ? ? 33 C9").GetPtr()); -#pragma endregion - -#pragma region Console - /*0x140202090*/ - FUNC_AT_ADDRESS(addr_CommandExecute, void(*)(void*, const char*), r5_patterns.PatternSearch("48 89 5C 24 ? 57 48 83 EC 20 48 8D 0D ? ? ? ? 41 8B D8").GetPtr()); - - /*0x14046FE90*/ - FUNC_AT_ADDRESS(addr_ConVar_IsFlagSet, bool(*)(int**, int), r5_patterns.PatternSearch("48 8B 41 48 85 50 38").GetPtr()); - - /*0x14046F490*/ - FUNC_AT_ADDRESS(addr_ConCommand_IsFlagSet, bool(*)(int*, int), r5_patterns.PatternSearch("85 51 38 0F 95 C0 C3").GetPtr()); -#pragma endregion - -#pragma region Squirrel - /*0x141057FD0*/ - FUNC_AT_ADDRESS(addr_SQVM_Print, void*, r5_patterns.PatternSearch("83 F8 01 48 8D 3D ? ? ? ?").OffsetSelf(0x3).FollowNearCallSelf(0x3, 0x7).GetPtr()); - - //DWORD64 p_SQVM_LoadScript = FindPattern("r5apex.exe", (const unsigned char*)"\x48\x89\x5C\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x48\x89\x4C\x24\x08\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\x6C", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); // For S0 and S1 - - /*0x141055630*/ - // For anything S2 and above (current S8 - FUNC_AT_ADDRESS(addr_SQVM_LoadScript, bool(*)(void*, const char*, const char*, int), r5_patterns.PatternSearch("48 8B C4 48 89 48 08 55 41 56 48 8D 68").GetPtr()); - - /*0x140C957E0*/ - FUNC_AT_ADDRESS(addr_SQVM_LoadRson, int(*)(const char*), r5_patterns.PatternSearch("4C 8B DC 49 89 5B 08 57 48 81 EC A0 00 00 00 33").GetPtr()); -#pragma endregion - -#pragma region NetChannel - /*0x1402655F0*/ - FUNC_AT_ADDRESS(addr_NET_ReceiveDatagram, bool(*)(int, void*, bool), r5_patterns.PatternSearch("48 89 74 24 18 48 89 7C 24 20 55 41 54 41 55 41 56 41 57 48 8D AC 24 50 EB").GetPtr()); - - /*0x1402662D0*/ - FUNC_AT_ADDRESS(addr_NET_SendDatagram, int(*)(SOCKET, const char*, int, int), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ? 05 ? ?").GetPtr()); - - /*0x14025F190*/ - FUNC_AT_ADDRESS(addr_NetChan_Shutdown, void(*)(void*, const char*, unsigned __int8, char), r5_patterns.StringSearch("Disconnect by server.\n").FindPatternSelf("E8 ? ? ? ? 4C 89 B3 ? ? ? ?", MemoryAddress::Direction::DOWN).FollowNearCallSelf().GetPtr()); -#pragma endregion - -#pragma region CHLClient - /*0x1405C0740*/ - FUNC_AT_ADDRESS(addr_CHLClient_FrameStageNotify, void(*)(void* rcx, int curStage), r5_patterns.PatternSearch("48 83 EC 28 89 15 ?? ?? ?? ??").GetPtr()); -#pragma endregion - -#pragma region CClientState - /*0x1418223E4*/ - FUNC_AT_ADDRESS(addr_m_bRestrictServerCommands, void*, r5_patterns.StringSearch("DevShotGenerator_Init()").FindPatternSelf("88 05", MemoryAddress::Direction::UP).ResolveRelativeAddressSelf(0x2).OffsetSelf(0x2).GetPtr()); -#pragma endregion - -#pragma region CVEngineServer - /*0x140315CF0*/ - FUNC_AT_ADDRESS(addr_CVEngineServer_IsPersistenceDataAvailable, bool(*)(__int64, int), r5_patterns.PatternSearch("3B 15 ?? ?? ?? ?? 7D 33").GetPtr()); -#pragma endregion - -#pragma region Utility - /*0x140295600*/ - FUNC_AT_ADDRESS(addr_MSG_EngineError, int(*)(char*, va_list), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 74 24 10 57 48 81 EC 30 08 00 00 48 8B DA").GetPtr()); - - /*0x1401B31C0*/ - FUNC_AT_ADDRESS(addr_MemAlloc_Wrapper, void* (*)(__int64), r5_patterns.StringSearch("ConversionModeMenu").FindPatternSelf("E8 ? ? ? ? 48", MemoryAddress::Direction::UP).FollowNearCallSelf().GetPtr()); -#pragma endregion - // Un-used atm. - // DWORD64 p_KeyValues_FindKey = /*1404744E0*/ reinterpret_cast(PatternScan("r5apex.exe", "40 56 57 41 57 48 81 EC ?? ?? ?? ?? 45")); - -#pragma region KeyValues -/*0x1404744E0*/ - FUNC_AT_ADDRESS(addr_KeyValues_FindKey, void* (*)(void*, const char*, bool), r5_patterns.PatternSearch("40 56 57 41 57 48 81 EC ?? ?? ?? ?? 45").GetPtr()); -#pragma endregion - - - void PrintHAddress() // Test the sigscan results - { - std::cout << "+--------------------------------------------------------+" << std::endl; - PRINT_ADDRESS("CSourceAppSystemGroup::Create", addr_CSourceAppSystemGroup_Create); - PRINT_ADDRESS("CommandExecute", addr_CommandExecute); - PRINT_ADDRESS("ConVar_IsFlagSet", addr_ConVar_IsFlagSet); - PRINT_ADDRESS("ConCommand_IsFlagSet", addr_ConCommand_IsFlagSet); - PRINT_ADDRESS("SQVM_Print", addr_SQVM_Print); - PRINT_ADDRESS("SQVM_LoadScript", addr_SQVM_LoadScript); - PRINT_ADDRESS("SQVM_LoadRson", addr_SQVM_LoadRson); - PRINT_ADDRESS("NET_ReceiveDatagram", addr_NET_ReceiveDatagram); - PRINT_ADDRESS("NET_SendDatagram ", addr_NET_SendDatagram); - PRINT_ADDRESS("NetChan_Shutdown ", addr_NetChan_Shutdown); - PRINT_ADDRESS("CHLClient::FrameStageNotify", addr_CHLClient_FrameStageNotify); - PRINT_ADDRESS("CVEngineServer::IsPersistenceDataAvailable", addr_CVEngineServer_IsPersistenceDataAvailable); - PRINT_ADDRESS("MSG_EngineError", addr_MSG_EngineError); - PRINT_ADDRESS("MemAlloc_Wrapper", addr_MemAlloc_Wrapper); - PRINT_ADDRESS("KeyValues_FindKey", addr_KeyValues_FindKey); - std::cout << "+--------------------------------------------------------+" << std::endl; - // TODO implement error handling when sigscan fails or result is 0 - } -} - -inline bool g_bDebugLoading = false; -inline bool g_bReturnAllFalse = false; -inline bool g_bDebugConsole = false; - -namespace Hooks -{ -#pragma region CSourceAppSystemGroup - char __fastcall CSourceAppSystemGroup_Create(__int64 a1); - - using CSourceAppSystemGroup_CreateFn = char(*)(__int64); - extern CSourceAppSystemGroup_CreateFn originalCSourceAppSystemGroup_Create; -#pragma endregion - -#pragma region CHLClient -// void __fastcall FrameStageNotify(CHLClient* rcx, ClientFrameStage_t curStage); - -// using FrameStageNotifyFn = void(__fastcall*)(CHLClient*, ClientFrameStage_t); -// extern FrameStageNotifyFn originalFrameStageNotify; -#pragma endregion - -#pragma region Squirrel - void* SQVM_Print(void* sqvm, char* fmt, ...); - __int64 SQVM_LoadRson(const char* rson_name); - bool SQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag); - - using SQVM_LoadRsonFn = __int64(*)(const char*); - extern SQVM_LoadRsonFn originalSQVM_LoadRson; - - using SQVM_LoadScriptFn = bool(*)(void*, const char*, const char*, int); - extern SQVM_LoadScriptFn originalSQVM_LoadScript; -#pragma endregion - -#pragma region CVEngineServer - bool IsPersistenceDataAvailable(__int64 thisptr, int client); - - using IsPersistenceDataAvailableFn = bool(*)(__int64, int); - extern IsPersistenceDataAvailableFn originalIsPersistenceDataAvailable; -#pragma endregion - -#pragma region NetChannel - bool NET_ReceiveDatagram(int sock, void* inpacket, bool raw); - unsigned int NET_SendDatagram(SOCKET s, const char* buf, int len, int flags); - - using NET_ReceiveDatagramFn = bool(*)(int, void*, bool); - extern NET_ReceiveDatagramFn originalNET_ReceiveDatagram; - - using NET_SendDatagramFn = unsigned int(*)(SOCKET, const char*, int, int); - extern NET_SendDatagramFn originalNET_SendDatagram; -#pragma endregion - -#pragma region ConVar - bool ConVar_IsFlagSet(int** cvar, int flag); - bool ConCommand_IsFlagSet(int* cmd, int flag); -#pragma endregion - -#pragma region Utility - int MSG_EngineError(char* fmt, va_list args); - - using MSG_EngineErrorFn = int(*)(char*, va_list); - extern MSG_EngineErrorFn originalMSG_EngineError; -#pragma endregion - - void InstallHooks(); - void RemoveHooks(); - void ToggleNetTrace(); - void ToggleDevCommands(); - void DedicatedPatch(); -} \ No newline at end of file diff --git a/r5dedicated/iconvar.cpp b/r5dedicated/iconvar.cpp deleted file mode 100644 index 75002dff..00000000 --- a/r5dedicated/iconvar.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -//----------------------------------------------------------------------------- -// Purpose: test each ConVar query before setting the cvar -// Input : **cvar - flag -// Output : true if change is not permitted, false if permitted -//----------------------------------------------------------------------------- - -bool Hooks::ConVar_IsFlagSet(int** cvar, int flag) -{ - int real_flags = *(*(cvar + (72 / (sizeof(void*)))) + (56 / sizeof(int))); - if (g_bDebugConsole) - { - printf("--------------------------------------------------\n"); - printf(" Flaged: %08X\n", real_flags); - } - // Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY - real_flags &= 0xFFFFBFFD; - if (g_bDebugConsole) - { - printf(" Masked: %08X\n", real_flags); - printf(" Verify: %08X\n", flag); - printf("--------------------------------------------------\n"); - } - if (flag & 0x80000) { return true; } - - if (!g_bReturnAllFalse) - { - return (real_flags & flag) != 0; - } - else - { - return false; - } -} - -//----------------------------------------------------------------------------- -// Purpose: test each ConCommand query before execution -// Input : *cmd - flag -// Output : true if execution is not permitted, false if permitted -//----------------------------------------------------------------------------- - -bool Hooks::ConCommand_IsFlagSet(int* cmd, int flag) -{ - int real_flags = *((cmd + (56 / sizeof(int)))); - if (g_bDebugConsole) - { - printf("--------------------------------------------------\n"); - printf(" Flaged: %08X\n", real_flags); - } - // Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY - real_flags &= 0xFFFFBFFD; - if (g_bDebugConsole) - { - printf(" Masked: %08X\n", real_flags); - printf(" Verify: %08X\n", flag); - printf("--------------------------------------------------\n"); - } - if (flag & 0x80000) { return true; } - - if (!g_bReturnAllFalse) - { - return(real_flags & flag) != 0; - } - else - { - return false; - } -} \ No newline at end of file diff --git a/r5dedicated/msgbox.cpp b/r5dedicated/msgbox.cpp deleted file mode 100644 index 58a7727b..00000000 --- a/r5dedicated/msgbox.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - MSG_EngineErrorFn originalMSG_EngineError = nullptr; -} - -//----------------------------------------------------------------------------- -// Engine Error message box -//----------------------------------------------------------------------------- - -int Hooks::MSG_EngineError(char* fmt, va_list args) -{ - printf("\nENGINE ERROR #####################################\n"); - vprintf(fmt, args); - - return originalMSG_EngineError(fmt, args); -} \ No newline at end of file diff --git a/r5dedicated/net.cpp b/r5dedicated/net.cpp deleted file mode 100644 index 84d2cd50..00000000 --- a/r5dedicated/net.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - NET_ReceiveDatagramFn originalNET_ReceiveDatagram = nullptr; - NET_SendDatagramFn originalNET_SendDatagram = nullptr; -} - -typedef unsigned __int64 QWORD; - -struct __declspec(align(8)) netpacket_t -{ - DWORD family_maybe; - sockaddr_in sin; - WORD sin_port; - BYTE gap16; - BYTE byte17; - DWORD source; - double received; - unsigned __int8* data; - QWORD label; - BYTE byte38; - QWORD qword40; - QWORD qword48; - BYTE gap50[8]; - QWORD qword58; - QWORD qword60; - QWORD qword68; - int less_than_12; - DWORD wiresize; - BYTE gap78[8]; - QWORD qword80; -}; - -//----------------------------------------------------------------------------- -// Purpose: hook and log the receive datagram -//----------------------------------------------------------------------------- -bool Hooks::NET_ReceiveDatagram(int sock, void* inpacket, bool raw) -{ - bool result = originalNET_ReceiveDatagram(sock, inpacket, raw); - if (result) - { - int i = NULL; - netpacket_t* pkt = (netpacket_t*)inpacket; - - /////////////////////////////////////////////////////////////////////////// - // Log received packet data - HexDump("[+] NET_ReceiveDatagram", 0, &pkt->data[i], pkt->wiresize); - } - - return result; -} - -//----------------------------------------------------------------------------- -// Purpose: hook and log send datagram -//----------------------------------------------------------------------------- -unsigned int Hooks::NET_SendDatagram(SOCKET s, const char* buf, int len, int flags) -{ - unsigned int result = originalNET_SendDatagram(s, buf, len, flags); - if (result) - { - /////////////////////////////////////////////////////////////////////////// - // Log transmitted packet data - HexDump("[+] NET_SendDatagram", 0, buf, len); - } - - return result; -} \ No newline at end of file diff --git a/r5dedicated/opcodes.h b/r5dedicated/opcodes.h deleted file mode 100644 index 9ba0943c..00000000 --- a/r5dedicated/opcodes.h +++ /dev/null @@ -1,117 +0,0 @@ -#pragma once - -inline HANDLE GameProcess = GetCurrentProcess(); -void SetCHostState(); - -namespace -{ - Module r5_op = Module("r5apex.exe"); // Create module class instance. - -#pragma region Origin - /*0x14032EEA0*/ - MemoryAddress Origin_Init = r5_op.PatternSearch("48 83 EC 28 80 3D ? ? ? 23 ? 0F 85 ? 02 ?"); - - /*0x140330290*/ - MemoryAddress Origin_SetState = r5_op.PatternSearch("48 81 EC 58 04 ? ? 80 3D ? ? ? ? ? 0F 84"); -#pragma endregion - -#pragma region Engine - /*0x14043FB90*/ - MemoryAddress dst002 = r5_op.PatternSearch("48 89 4C 24 08 56 41 55 48 81 EC 68 03 ? ? 4C"); - - /*0x14022A4A0*/ - MemoryAddress dst004 = r5_op.PatternSearch("48 83 EC 38 0F 29 74 24 20 48 89 5C 24 40 48 8B"); - - /*0x140238DA0*/ - MemoryAddress Host_NewGame = r5_op.PatternSearch("48 8B C4 ? 41 54 41 ? 48 81 EC ? ? ? ? F2"); -#pragma endregion - -#pragma region NetChannel - /*0x14030D000*/ - MemoryAddress CServer_Auth = r5_op.PatternSearch("40 55 57 41 55 41 57 48 8D AC 24 ? ? ? ?"); -#pragma endregion - -#pragma region FairFight - /*0x140303AE0*/ - MemoryAddress FairFight_Init = r5_op.PatternSearch("40 53 48 83 EC 20 8B 81 B0 03 ? ? 48 8B D9 C6"); -#pragma endregion - - - // TODO: create patterns instead and rename to function names. - // Renderer - MemoryAddress r0 = 0x00000001402FE280; // - MemoryAddress gCShaderGlue__Init = 0x00000001403B3A50; // - MemoryAddress gMatSync = 0x00000001403DEE90; // - MemoryAddress gCMaterialSystem__MatsysMode_Init = 0x00000001403BD120; // - MemoryAddress r4 = 0x0000000140404380; // - MemoryAddress r5 = 0x000000014040D850; // - MemoryAddress r6 = 0x0000000140413260; // - MemoryAddress r7 = 0x00000001404093F0; // - MemoryAddress r8 = 0x00000001403D2E60; // - MemoryAddress d3d11init = 0x000000014043CDF0; // - - // Engine - MemoryAddress gHost_Init_0 = 0x0000000140236E40; // main Host_Init()? - MemoryAddress e1 = 0x0000000140FB2F10; // also used by CServerGameDLL - MemoryAddress addr_CEngine_Frame = 0x00000001402970E0; - MemoryAddress e3 = 0x0000000140231C00; - MemoryAddress e4 = 0x0000000140BE1970; - MemoryAddress e5 = 0x0000000140DBBAF0; - MemoryAddress e6 = 0x0000000140DBE610; - MemoryAddress e7 = 0x000000014044AFA0; - MemoryAddress e8 = 0x000000014027EC50; // RenderFrame? - MemoryAddress gCEngineAPI__Init = 0x0000000140342FB0; // - MemoryAddress gCEngineAPI__ModInit = 0x0000000140343DE0; // - MemoryAddress gCEngineAPI__Connect = 0x0000000140342BA0; // - MemoryAddress gCEngineAPI__OnStartup = 0x0000000140343860; // - MemoryAddress gCSourceAppSystemGroup__Create = 0x000000014044AFA0; // - MemoryAddress gCShaderSystem__Init = 0x00000001403DF870; // - MemoryAddress gInitMaterialSystem = 0x000000014024B390; // - MemoryAddress gCVideoMode_Common__DrawStartupGraphic = 0x000000014027F0F0; // - MemoryAddress gShaderDispatch = 0x00000001403EE5C0; - MemoryAddress gShaderCreate = 0x00000001403ECD00; // - MemoryAddress gTextureCreate = 0x00000001403EDCD0; - - MemoryAddress gCShaderSystem__9 = 0x00000001403DFC30; - MemoryAddress gBSP_LUMP_INIT = 0x00000001402546F0; // BSP. - - - MemoryAddress e9 = 0x00000001404066E0; - MemoryAddress e10 = 0x00000001403B49E0; // CMaterialGlue? - - // SERVER - MemoryAddress gHost_Init_1 = 0x0000000140237B00; // server Host_Init()? - MemoryAddress s1 = 0x0000000140231C00; // _Host_RunFrame() with inlined CFrameTimer::MarkFrame()? - MemoryAddress s2 = 0x00000001402312A0; // server HeartBeat? (baseserver.cpp) - MemoryAddress s3 = 0x0000000140FB36D0; // TEMP?? - - // CLIENT - MemoryAddress gHost_Init_2 = 0x0000000140236640; // client Host_Init()? - MemoryAddress gCGame__CreateGameWindow = 0x0000000140299100; // - MemoryAddress c2 = 0x00000001403F4360; // 1403DF870 --> 1403F4360 - MemoryAddress c3 = 0x00000001403F8A80; // 1403DF870 --> 1403F8A40 - MemoryAddress gCHLClient__1000 = 0x00000001405C27B0; // CHLClient + 1000 - MemoryAddress gCHLClient__HudMessage = 0x00000001405BAC00; // CHudMessage - MemoryAddress c6 = 0x00000001403CA2D0; // - - // VGUI - MemoryAddress gCEngineVGui__Init = 0x0000000140282E40; // jumptable - MemoryAddress gCEngineVGui__OnLevelLoadingStarted = 0x00000001402830D0; - MemoryAddress SCR_BeginLoadingPlaque = 0x000000014023E870; - - - void PrintOAddress() // Test the sigscan results - { - std::cout << "+--------------------------------------------------------+" << std::endl; - PRINT_ADDRESS("Origin_Init", Origin_Init.GetPtr()); - PRINT_ADDRESS("Origin_SetState", Origin_SetState.GetPtr()); - PRINT_ADDRESS("dst002", dst002.GetPtr()); - PRINT_ADDRESS("dst004", dst004.GetPtr()); - PRINT_ADDRESS("Host_NewGame", Host_NewGame.GetPtr()); - PRINT_ADDRESS("CServer_Auth", CServer_Auth.GetPtr()); - PRINT_ADDRESS("FairFight_Init", FairFight_Init.GetPtr()); - std::cout << "+--------------------------------------------------------+" << std::endl; - - // TODO implement error handling when sigscan fails or result is 0 - } -} \ No newline at end of file diff --git a/r5dedicated/pch.cpp b/r5dedicated/pch.cpp deleted file mode 100644 index 17305716..00000000 --- a/r5dedicated/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" \ No newline at end of file diff --git a/r5dedicated/pch.h b/r5dedicated/pch.h deleted file mode 100644 index 9a8b7cc6..00000000 --- a/r5dedicated/pch.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#pragma message("[DEDICATED] pre-compiling headers.\n") - -#define WIN32_LEAN_AND_MEAN // Prevent winsock2 redefinition. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -// Our headers - -#include "spdlog.h" -#include "sinks/basic_file_sink.h" -#include "sinks/stdout_sinks.h" -#include "sinks/ostream_sink.h" -#include "utility.h" -#include "httplib.h" -#include "json.hpp" - -#include "address.h" - -#pragma once - -#define FUNC_AT_ADDRESS(name, funcbody, addr) \ - using _##name = funcbody; \ - _##name name = (funcbody)addr \ - -#define PRINT_ADDRESS(name, address) std::cout << name << ": " << std::hex << std::uppercase << address << std::endl; diff --git a/r5dedicated/r5dedicated.def b/r5dedicated/r5dedicated.def deleted file mode 100644 index adb0127b..00000000 --- a/r5dedicated/r5dedicated.def +++ /dev/null @@ -1,4 +0,0 @@ -LIBRARY dedicated - -EXPORTS - DummyExport @1 \ No newline at end of file diff --git a/r5dedicated/r5dedicated.vcxproj b/r5dedicated/r5dedicated.vcxproj deleted file mode 100644 index 7ef975cb..00000000 --- a/r5dedicated/r5dedicated.vcxproj +++ /dev/null @@ -1,318 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {71988d92-343c-49ab-b52b-0ae0e83b0401} - r5dedicated - 10.0 - - - - DynamicLibrary - true - v142 - Unicode - - - DynamicLibrary - false - v142 - true - Unicode - - - DynamicLibrary - true - v142 - MultiByte - - - DynamicLibrary - false - v142 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - true - dedicated - - - false - dedicated - - - true - dedicated - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)external\minhook\include;$(SolutionDir)external\spdlog\include;$(SolutionDir)shared\include;$(SolutionDir)r5dedicated;$(IncludePath) - $(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(SolutionDir)external\minhook\lib\$(Configuration);$(LibraryPath) - $(SolutionDir)bin\$(Configuration)\ - $(SolutionDir)build\$(ProjectName)\$(Configuration)\ - - - false - dedicated - $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)external\minhook\include;$(SolutionDir)external\spdlog\include;$(SolutionDir)shared\include;$(SolutionDir)r5dedicated;$(IncludePath) - $(SolutionDir)external\minhook\lib\$(Configuration);$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(LibraryPath) - $(SolutionDir)bin\$(Configuration)\ - $(SolutionDir)build\$(ProjectName)\$(Configuration)\ - - - - Level3 - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - Default - Default - - - Console - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - Default - Default - - - Console - true - true - true - - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp17 - stdc17 - Use - pch.h - $(IntDir)$(TargetName).pch - MultiThreadedDebug - - - Console - true - Minhook.x64.lib - r5dedicated.def - - - if not EXIST $(SolutionDir)external\minhook\lib\$(Configuration)\Minhook.x64.lib ( -"$(DevEnvDir)devenv" "$(SolutionDir)apex.sln" /Rebuild $(Configuration) /project "$(SolutionDir)external\minhook\libMinHook.vcxproj" -) - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpp17 - stdc17 - Disabled - Speed - false - true - Use - pch.h - $(IntDir)$(TargetName).pch - - - Console - true - true - true - Minhook.x64.lib - r5dedicated.def - - - if not EXIST $(SolutionDir)external\minhook\lib\$(Configuration)\Minhook.x64.lib ( -"$(DevEnvDir)devenv" "$(SolutionDir)apex.sln" /Rebuild $(Configuration) /project "$(SolutionDir)external\minhook\libMinHook.vcxproj" -) - - - - - - - - - - - - - - - Create - Create - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/r5dedicated/r5dedicated.vcxproj.filters b/r5dedicated/r5dedicated.vcxproj.filters deleted file mode 100644 index 43adb38d..00000000 --- a/r5dedicated/r5dedicated.vcxproj.filters +++ /dev/null @@ -1,450 +0,0 @@ - - - - - {b5a2491a-0f04-4b55-bc24-bbdd9cd6fc2e} - - - {ef65ed91-551f-4f5d-b2a9-efc26e1af0f6} - - - {22b58a18-d1d7-4e3e-b5de-d9338f3177d3} - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {a0165b58-06b1-4b6a-b5d1-5d643517ad14} - - - {35b40ed1-12bd-4bcf-9c05-5a42a0096619} - - - {a7199092-e8a9-49fa-97e1-b2d0ea21001b} - - - {bd5aef1a-dad8-45a0-85f2-82bc8f86bed8} - - - {c947552e-2623-4672-b3fa-3b50fe819628} - - - {19dd0a10-5185-4b03-9466-693b86a17dff} - - - {cb271201-f518-4dc4-9ab4-5d7f6a54c175} - - - {12259d0c-172a-4a26-a1d4-5d784f4c3d25} - - - {13bdf72b-f7c0-4658-9899-932f31d03da7} - - - {eaefe9b7-d14d-48b6-878a-53a5ada7454b} - - - {245e8064-9b24-4631-9326-340dfb761fde} - - - {485b5648-149f-4664-a961-be9cd520e9e3} - - - {31cdde4d-3641-497c-9b34-20d3d7c89d87} - - - {338a4fb7-7519-4628-9206-679d33824965} - - - {06affed3-5a59-4b95-88ca-72d92c91909b} - - - {05e6e9a7-801b-49b0-9c5a-21c4868befb7} - - - {cc424eef-0c7a-4fb0-9d84-30bf8db2e253} - - - {74afa89f-72af-4e13-aa90-70f7a1957154} - - - {9381fa63-cf89-4980-8e5a-bf6e43cb2283} - - - {7fd080e8-390a-430b-a94c-e19c5792bf10} - - - {e7e154b6-398e-42f9-bfb9-e80cd306254e} - - - {3c89e0ef-e415-4a91-86b7-2a04a5f03340} - - - - - shared\include - - - shared\include - - - shared\include - - - shared\include - - - core\include - - - core\include - - - core\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include - - - shared\libraries\spdlog\include\cfg - - - shared\libraries\spdlog\include\cfg - - - shared\libraries\spdlog\include\cfg - - - shared\libraries\spdlog\include\cfg - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\details - - - shared\libraries\spdlog\include\fmt - - - shared\libraries\spdlog\include\fmt - - - shared\libraries\spdlog\include\fmt - - - shared\libraries\spdlog\include\fmt - - - shared\libraries\spdlog\include\fmt - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\fmt\bundled - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\spdlog\include\sinks - - - shared\libraries\minhook\include - - - hooks\include - - - hooks\include - - - r5-sdk\include - - - r5-sdk\include - - - - - core - - - core - - - core - - - hooks\netchannel - - - hooks\cvengineserver - - - hooks\squirrel - - - hooks\other - - - shared - - - hooks\iconvar - - - hooks - - - hooks - - - hooks\csourceappsystemgroup - - - r5-sdk\src - - - - - core\resource - - - \ No newline at end of file diff --git a/r5dedicated/sqvm.cpp b/r5dedicated/sqvm.cpp deleted file mode 100644 index 137dd52f..00000000 --- a/r5dedicated/sqvm.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - SQVM_LoadRsonFn originalSQVM_LoadRson = nullptr; - SQVM_LoadScriptFn originalSQVM_LoadScript = nullptr; -} - -//--------------------------------------------------------------------------------- -// Purpose: prints the output of each VM to the console -//--------------------------------------------------------------------------------- -void* Hooks::SQVM_Print(void* sqvm, char* fmt, ...) -{ - int vmIdx = *(int*)((std::uintptr_t)sqvm + 0x18); - - static char buf[1024]; - static std::string vmType[3] = { "Script(S):", "Script(C):", "Script(U):" }; - - static auto wconsole = spdlog::stdout_logger_mt("sqvm_wconsole"); // windows console - - std::string vmStr = vmType[vmIdx].c_str(); - - wconsole->set_pattern("[%S.%e] %v"); - wconsole->set_level(spdlog::level::debug); - - va_list args; - va_start(args, fmt); - - vsnprintf(buf, sizeof(buf), fmt, args); - - buf[sizeof(buf) - 1] = 0; - va_end(args); - - vmStr.append(buf); - wconsole->debug(vmStr); - - return NULL; -} - -//--------------------------------------------------------------------------------- -// Purpose: loads the include file from the mods directory -//--------------------------------------------------------------------------------- -__int64 Hooks::SQVM_LoadRson(const char* rson_name) -{ - char filepath[MAX_PATH] = { 0 }; - sprintf_s(filepath, MAX_PATH, "platform\\%s", rson_name); - - /////////////////////////////////////////////////////////////////////////////// - // Flip forward slashes in filepath to windows-style backslash - for (int i = 0; i < strlen(filepath); i++) - { - if (filepath[i] == '/') - { - filepath[i] = '\\'; - } - } - - /////////////////////////////////////////////////////////////////////////////// - // Returns the new path if the rson exists on the disk - if (FileExists(filepath) && originalSQVM_LoadRson(rson_name)) - { - printf("\n"); - printf("##################################################\n"); - printf("] '%s'\n", filepath); - printf("##################################################\n"); - printf("\n"); - - return originalSQVM_LoadRson(filepath); - } - - printf("\n"); - printf("##################################################\n"); - printf("] '%s'\n", rson_name); - printf("##################################################\n"); - printf("\n"); - - return originalSQVM_LoadRson(rson_name); -} - -//--------------------------------------------------------------------------------- -// Purpose: loads the script file from the mods directory -//--------------------------------------------------------------------------------- -bool Hooks::SQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag) -{ - char filepath[MAX_PATH] = { 0 }; - sprintf_s(filepath, MAX_PATH, "platform\\%s", script_path); - - /////////////////////////////////////////////////////////////////////////////// - // Flip forward slashes in filepath to windows-style backslash - for (int i = 0; i < strlen(filepath); i++) - { - if (filepath[i] == '/') - { - filepath[i] = '\\'; - } - } - if (g_bDebugLoading) - { - printf(" [+] Loading SQVM Script '%s' ...\n", filepath); - } - /////////////////////////////////////////////////////////////////////////////// - // Returns true if the script exists on the disk - if (FileExists(filepath) && originalSQVM_LoadScript(sqvm, filepath, script_name, flag)) - { - return true; - } - if (g_bDebugLoading) - { - printf(" [!] FAILED. Try SP / VPK for '%s'\n", filepath); - } - - return originalSQVM_LoadScript(sqvm, script_path, script_name, flag); -} \ No newline at end of file diff --git a/r5dev/bsplib/bsplib.cpp b/r5dev/bsplib/bsplib.cpp new file mode 100644 index 00000000..1e84ec60 --- /dev/null +++ b/r5dev/bsplib/bsplib.cpp @@ -0,0 +1,339 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "common/psuedodefs.h" +#include "bsplib/bsplib.h" + +//----------------------------------------------------------------------------- +// Purpose: calculates the view frustum culling data per static prop +//----------------------------------------------------------------------------- +__int64 __fastcall HCalcPropStaticFrustumCulling(__int64 a1, __int64 a2, unsigned int a3, unsigned int a4, __int64 a5, __int64 a6, __int64 a7) +{ + //float v9; // xmm6_4 + //char v10; // r13 + //float v11; // xmm7_4 + //__int64 v12; // rsi + //__int64 v13; // rdx + //__int64 v14; // rbx + //unsigned __int8 v15; // r8 + //const char* v16; // rax + //int v17; // eax + //char v18; // dl + //bool v19; // cc + //char v20; // al + //__int16 v21; // ax + //__int64 v22; // rax + //__m128 v23; // xmm9 + //__m128 v24; // xmm10 + //__m128 v25; // xmm11 + //unsigned __int64 v26; // rbx + //__m128 v27; // xmm0 + //__m128 v28; // xmm8 + //__int64 v29; // rax + //__int64 v30; // rcx + //__int64 v31; // xmm1_8 + //int v32; // er8 + //__m128 v33; // xmm0 + //__int64 v34; // rcx + //unsigned __int64 v35; // rdx + //__int64 v36; // rax + //__int64 v37; // rax + //unsigned __int64 v38; // rcx + //__int64 v39; // rax + //unsigned __int64 v40; // rcx + //__int64 v41; // rax + //float v42; // xmm2_4 + //__int64 v43; // rbx + //float v44; // xmm2_4 + //float v45; // xmm4_4 + //__m128i v46; // xmm1 + //__m128i v47; // xmm2 + //__m128i v48; // xmm3 + //unsigned int v49; // eax + //float v50; // xmm0_4 + //float v51; // xmm2_4 + //float v52; // xmm1_4 + //float v53; // xmm2_4 + //unsigned __int8 v54; // si + //__int64 v55; // rax + //__int64 v56; // rcx + //int v57; // eax + //__int64 v58; // rcx + //int v59; // edx + //__int64 v60; // rax + //__int64 v61; // r13 + //int v62; // eax + //unsigned __int64 v63; // r13 + //__int64 v64; // rdx + //int v65; // er14 + //unsigned __int64 v66; // rdi + //__int64 v67; // r13 + //__int64 v68; // r15 + //void** v69; // rbx + //__int64 v70; // rcx + //__int64 result; // rax + //__m128 v72; // [rsp+38h] [rbp-D0h] BYREF + //__int64 v73; // [rsp+48h] [rbp-C0h] + //__int64 v74; // [rsp+50h] [rbp-B8h] + //__m128 v75; // [rsp+58h] [rbp-B0h] BYREF + //__int64 v76; // [rsp+68h] [rbp-A0h] + //__int64 v77; // [rsp+78h] [rbp-90h] + //__m128 v78[3]; // [rsp+88h] [rbp-80h] BYREF + //char Destination[376]; // [rsp+B8h] [rbp-50h] BYREF + //__int64 v80; // [rsp+278h] [rbp+170h] + //int v83; // [rsp+288h] [rbp+180h] + //int v84; // [rsp+290h] [rbp+188h] + //__int64 v85; // [rsp+298h] [rbp+190h] + //__int64 v86; // [rsp+2A8h] [rbp+1A0h] + + //v9 = 1.0; + //v10 = a4; + //*(_QWORD*)(a1 + 20) = *(_QWORD*)a5; + //*(_DWORD*)(a1 + 28) = *(_DWORD*)(a5 + 8); + //*(_DWORD*)(a1 + 8) = a4; + //v11 = *(float*)(a5 + 24); + //v12 = a4 >> 1; + //*(float*)(a1 + 12) = 1.0 / (float)(v11 * v11); + //v13 = *(unsigned __int16*)(a7 + 320); + //*(_WORD*)a1 = v13; + //v14 = (*(__int64(__fastcall**)(__int64, __int64, _QWORD))(*(_QWORD*)qword_14D40B328 + 104i64))(qword_14D40B328, v13, 0i64); + //v85 = v14; + //if ((*(_BYTE*)(v14 + 156) & 0x10) == 0 && dword_1696A9D20 < 100) + // ++dword_1696A9D20; + //v15 = *(_BYTE*)(a5 + 30); + //if (v15 > 2u && (unsigned __int8)(v15 - 6) > 2u) + //{ + // v16 = (const char*)(*((__int64(__fastcall**)(void**, __int64))g_CModelLoader + 4))(&g_CModelLoader, a7); + // strncpy_s(Destination, v16, 0x104ui64); + // v15 = 0; + //} + //v17 = *(unsigned __int8*)(a5 + 32); + //*(_BYTE*)(a1 + 4) = v17; + //v18 = v17; + //v19 = v17 < *(_DWORD*)(v14 + 228); + //*(_BYTE*)(a1 + 5) = v15; + //if (!v19) + // v18 = 0; + //*(_BYTE*)(a1 + 4) = v18; + //v20 = *(_BYTE*)(a5 + 31); + //if ((v20 & 4) != 0) + //{ + // v15 |= 0x40u; + // *(_BYTE*)(a1 + 5) = v15; + // v20 = *(_BYTE*)(a5 + 31); + //} + //if ((v20 & 8) != 0) + //{ + // v15 |= 0x20u; + // *(_BYTE*)(a1 + 5) = v15; + // v20 = *(_BYTE*)(a5 + 31); + //} + //if ((v20 & 0x30) != 16) + // *(_BYTE*)(a1 + 5) = v15 | 0x10; + //v21 = 0; + //if (*(_WORD*)(a5 + 34) != 0xFFFF) + // v21 = *(_WORD*)(a5 + 34); + //*(_WORD*)(a1 + 2) = v21; + //sub_1404365A0(v78, a5, a5 + 12, 0); + //v22 = qword_141744EA8; + //v23 = v78[0]; + //v24 = v78[1]; + //v25 = v78[2]; + //v26 = (unsigned __int64)(unsigned int)v12 << 6; + //*(__m128*)(v26 + qword_141744EA8) = v78[0]; + //*(__m128*)(v26 + v22 + 16) = v24; + //*(__m128*)(v26 + v22 + 32) = v25; + //v27 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(_mm_unpacklo_epi8(_mm_cvtsi32_si128(*(_DWORD*)(a5 + 52)), (__m128i)0i64), (__m128i)0i64)), (__m128)xmmword_1415BD270); + //v75 = v27; + //*(double*)v27.m128_u64 = _mm_castsi128_ps(sub_140270130(&v75)); + //v28 = v27; + //*(__m128*)(v26 + qword_141744EA8 + 48) = v27; + //((void(__fastcall*)(void***, __int64, __int64*, char*))off_141731448[10])(&off_141731448, a7, (long long*)&v72, (char*)&v72 + 12); + //sub_14028F170((unsigned int)&v75, (unsigned int)&v75.m128_u32[3], (__m128*)v78, (__m128i*)&v72, (__m128i*)&v72 + 12); + //v29 = qword_141744EA0; + //v30 = 3 * v12; + //v31 = v76; + //*(__m128*)(qword_141744EA0 + 8 * v30) = v75; + //*(_QWORD*)(v29 + 8 * v30 + 16) = v31; + //if ((v10 & 1) != 0) + //{ + // v32 = dword_141744EBC; + // v33 = v72; + // *(_DWORD*)a2 = *(_DWORD*)(a6 + 48); + // *(_DWORD*)(a2 + 4) = *(_DWORD*)(a6 + 52); + // *(_QWORD*)(a2 + 8) = 0i64; + // v34 = 3i64 * (unsigned int)(v32 + v12); + // v35 = (unsigned __int64)(unsigned int)(v32 + v12) << 6; + // v36 = qword_141744EA0; + // *(__m128*)(qword_141744EA0 + 8 * v34) = v33; + // *(_QWORD*)(v36 + 8 * v34 + 16) = v73; + // v37 = qword_141744EA8; + // v38 = (unsigned __int64)(unsigned int)(v12 + 2 * v32) << 6; + // *(__m128*)(v35 + qword_141744EA8) = v23; + // *(__m128*)(v35 + v37 + 16) = v24; + // *(__m128*)(v35 + v37 + 32) = v25; + // *(__m128*)(v35 + qword_141744EA8 + 48) = v28; + // v39 = qword_141744EA8; + // *(__m128*)(v38 + qword_141744EA8) = v23; + // *(__m128*)(v38 + v39 + 16) = v24; + // *(__m128*)(v38 + v39 + 32) = v25; + // *(__m128*)(v38 + qword_141744EA8 + 48) = v28; + // v40 = (unsigned __int64)(unsigned int)(v32 + v12 + 2 * v32) << 6; + // *(__m128*)(v40 + qword_141744EA8 + 48) = v28; + // v41 = qword_141744EA8; + // *(__m128*)(v40 + qword_141744EA8) = *(__m128*)a6; + // *(__m128*)(v40 + v41 + 16) = *(__m128*)(a6 + 16); + // *(__m128*)(v40 + v41 + 32) = *(__m128*)(a6 + 32); + //} + //v42 = *(float*)(a5 + 36); + //v43 = v85; + //if (v42 <= 0.0) + //{ + // if ((*(_DWORD*)(v85 + 156) & 0x800) != 0) + // { + // v42 = 227023.36; + // } + // else + // { + // v44 = *(float*)(v85 + 364); + // if (v44 <= 0.0) + // v42 = fmaxf( + // (float)((float)(sqrtf( + // (float)((float)((float)(*(float*)&v72 - *((float*)&v72 + 3)) * (float)(*(float*)&v72 - *((float*)&v72 + 3))) + // + (float)((float)(*((float*)&v72 + 1) - *(float*)&v73) * (float)(*((float*)&v72 + 1) - *(float*)&v73))) + // + (float)((float)(*((float*)&v72 + 2) - *((float*)&v73 + 1)) * (float)(*((float*)&v72 + 2) - *((float*)&v73 + 1)))) + // * 0.5) + // * v11) + // * g_pCvar->FindVar("model_defaultFadeDistScale")->m_pParent->m_flValue, + // g_pCvar->FindVar("model_defaultFadeDistMin")->m_pParent->m_flValue); + // else + // v42 = v44 * v11; + // } + //} + //v45 = fmaxf(v42, 100.0); + //*(float*)(a1 + 16) = v45 * v45; + //v46 = (__m128i) * (unsigned int*)(v85 + 368); + //if (*(float*)v46.m128i_i32 <= 0.0) + //{ + // LOWORD(v49) = 0; + //} + //else + //{ + // *(float*)v46.m128i_i32 = *(float*)v46.m128i_i32 * *(float*)v46.m128i_i32; + // v47 = v46; + // *(float*)v47.m128i_i32 = fmaxf(*(float*)v46.m128i_i32, 1.0004883); + // v48 = v47; + // *(float*)v48.m128i_i32 = fminf(*(float*)v47.m128i_i32, 4293918700.0); + // v49 = (unsigned int)(_mm_cvtsi128_si32(v48) - 1065351168) >> 12; + //} + //*(_WORD*)(a1 + 6) = v49; + //v50 = *(float*)(qword_141744EA0 + 24 * ((unsigned __int64)*(unsigned int*)(a1 + 8) >> 1) + 8) + // - *(float*)(qword_141744EA0 + 24 * ((unsigned __int64)*(unsigned int*)(a1 + 8) >> 1) + 20); + //v51 = *(float*)(qword_141744EA0 + 24 * ((unsigned __int64)*(unsigned int*)(a1 + 8) >> 1) + 4) + // - *(float*)(qword_141744EA0 + 24 * ((unsigned __int64)*(unsigned int*)(a1 + 8) >> 1) + 16); + //v52 = *(float*)(qword_141744EA0 + 24 * ((unsigned __int64)*(unsigned int*)(a1 + 8) >> 1)) + // - *(float*)(qword_141744EA0 + 24 * ((unsigned __int64)*(unsigned int*)(a1 + 8) >> 1) + 12); + //v53 = (float)((float)(v51 * v51) + (float)(v52 * v52)) + (float)(v50 * v50); + //if (v45 >= 227023.363449684) + // v9 = g_pCvar->FindVar("staticProp_no_fade_scalar")->m_pParent->m_flValue; + //v54 = 0; + //*(float*)(qword_141744E88 + 8i64 * a3) = v9 * (float)(1.0 / (float)(v53 * g_pCvar->FindVar("staticProp_gather_size_weight")->m_pParent->m_flValue)); + //v55 = qword_141744E88; + //*(_BYTE*)(qword_141744E88 + 8i64 * a3 + 4) &= 0xFEu; + //*(_BYTE*)(v55 + 8i64 * a3 + 4) |= v45 >= 227023.363449684; + //v56 = *(_QWORD*)(*(__int64(__fastcall**)(__int64, _QWORD))(*(_QWORD*)qword_14D40B328 + 160i64))(qword_14D40B328, *(unsigned __int16*)(a7 + 320)); + //v57 = *(unsigned __int16*)(a5 + 32); + //v77 = v56; + //v58 = v85 + *(int*)(v85 + 232) + 2i64 * v57 * *(_DWORD*)(v85 + 224); + //v59 = 0; + //v86 = v58; + //v84 = 0; + //if (*(int*)(v85 + 236) <= 0) + // return 0i64; + //v60 = 0i64; + //v74 = 0i64; + //do + //{ + // v61 = v60 + *(int*)(v43 + 240); + // v62 = 0; + // v63 = v43 + v61; + // v83 = 0; + // v75.m128_u64[0] = v63; + // if (*(int*)(v63 + 4) > 0) + // { + // v64 = 0i64; + // v80 = 0i64; + // do + // { + // v65 = 0; + // v66 = v63 + v64 + *(int*)(v63 + 12); + // if (*(int*)(v66 + 76) > 0) + // { + // v67 = v77; + // v68 = 0i64; + // do + // { + // v69 = *(void***)(v67 + 8i64 * *(__int16*)(v58 + 2i64 * *(int*)(v68 + *(int*)(v66 + 80) + v66))); + // if (!(*((unsigned __int8(__fastcall**)(void**)) * v69 + 86))(v69)) + // { + // if (!(*((unsigned __int8(__fastcall**)(void**)) * v69 + 32))(v69) && (*((unsigned __int8(__fastcall**)(void**)) * v69 + 31))(v69)) + // { + // v70 = 0i64; + // if (dword_141744EE8) + // { + // while (off_141744E70[v70 + 16] != v69) + // { + // v70 = (unsigned int)(v70 + 1); + // if ((unsigned int)v70 >= dword_141744EE8) + // goto LABEL_42; + // } + // } + // else + // { + // LABEL_42: + // off_141744E70[(unsigned int)dword_141744EE8++ + 16] = v69; + // } + // } + // if ((*((unsigned __int8(__fastcall**)(void**)) * v69 + 21))(v69) && (*((unsigned int(__fastcall**)(void**, __int64)) * v69 + 18))(v69, 1i64)) + // *(_BYTE*)(a1 + 5) |= 0x80u; + // if (!(*((unsigned __int8(__fastcall**)(void**)) * v69 + 48))(v69) && (*((unsigned int(__fastcall**)(void**, __int64)) * v69 + 18))(v69, 21844i64)) + // v54 |= 2u; + // v54 |= (*((unsigned __int8(__fastcall**)(void**)) * v69 + 48))(v69) != 0; + // } + // v58 = v86; + // ++v65; + // v68 += 92i64; + // } while (v65 < *(_DWORD*)(v66 + 76)); + // v63 = v75.m128_u64[0]; + // v62 = v83; + // v64 = v80; + // } + // ++v62; + // v64 += 136i64; + // v83 = v62; + // v80 = v64; + // } while (v62 < *(_DWORD*)(v63 + 4)); + // v43 = v85; + // v59 = v84; + // } + // ++v59; + // v60 = v74 + 16; + // v84 = v59; + // v74 += 16i64; + //} while (v59 < *(_DWORD*)(v43 + 236)); + //result = v54; + //if (v54) + // *(_BYTE*)(a1 + 5) &= 0x7Fu; + //return result; + +return NULL; +} + +void BspLib_Attach() +{ + DetourAttach((LPVOID*)&CalcPropStaticFrustumCulling, &HCalcPropStaticFrustumCulling); +} + +void BspLib_Detach() +{ + DetourDetach((LPVOID*)&CalcPropStaticFrustumCulling, &HCalcPropStaticFrustumCulling); +} diff --git a/r5dev/bsplib/bsplib.h b/r5dev/bsplib/bsplib.h new file mode 100644 index 00000000..d5f5f57c --- /dev/null +++ b/r5dev/bsplib/bsplib.h @@ -0,0 +1,45 @@ +#pragma once + +namespace +{ + //static auto g_CModelLoader = ADDRESS(0x14173B210).RCast(); + + //static auto dword_1696A9D20 = ADDRESS(0x14D40B328).RCast(); + //static auto dword_141744EE8 = ADDRESS(0x141744EE8).RCast(); + //static auto dword_141744EBC = ADDRESS(0x141744EBC).RCast(); + + //static auto qword_14D40B328 = ADDRESS(0x14D40B328).RCast(); + //static auto qword_141744EA8 = ADDRESS(0x141744EA8).RCast(); + //static auto qword_141744EA0 = ADDRESS(0x141744EA0).RCast(); + //static auto qword_141744E88 = ADDRESS(0x141744E88).RCast(); + + //static auto off_141744E70 = ADDRESS(0x141744E70).RCast(); + //static auto off_141731448 = ADDRESS(0x141744EA8).RCast(); + + //__m128 xmmword_1415BD270 = _mm_castsi128_ps(_mm_set_epi32(0x3B808081, 0x3B808081, 0x3B808081, 0x3B808081)); // xmmword_1415BD270 + + //static auto sub_1404365A0 = ADDRESS(0x1404365A0).RCast(); // Prototype is most likely incorrect: 'local variable allocation has failed, the output may be wrong!' + //static auto sub_140270130 = ADDRESS(0x140270130).RCast<__m128 (*)(__m128*)>(); + //static auto sub_14028F170 = ADDRESS(0x14028F170).RCast(); + + ADDRESS p_CalcPropStaticFrustumCulling = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\xC4\x44\x89\x40\x18\x48\x89\x50\x10\x55", "xxxxxxxxxxxx"); + __int64 (*CalcPropStaticFrustumCulling)(__int64 a1, __int64 a2, unsigned int a3, unsigned int a4, __int64 a5, __int64 a6, __int64 a7) = (__int64 (*)(__int64 a1, __int64 a2, unsigned int a3, unsigned int a4, __int64 a5, __int64 a6, __int64 a7))p_CalcPropStaticFrustumCulling.GetPtr(); /*48 8B C4 44 89 40 18 48 89 50 10 55*/ +} + +__int64 __fastcall HCalcPropStaticFrustumCulling(__int64 a1, __int64 a2, unsigned int a3, unsigned int a4, __int64 a5, __int64 a6, __int64 a7); + +void BspLib_Attach(); +void BspLib_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HBspLib : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CalcPropStaticFrustumCulling : 0x" << std::hex << std::uppercase << p_CalcPropStaticFrustumCulling.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HBspLib); diff --git a/r5dev/client/IVEngineClient.cpp b/r5dev/client/IVEngineClient.cpp new file mode 100644 index 00000000..c9151c73 --- /dev/null +++ b/r5dev/client/IVEngineClient.cpp @@ -0,0 +1,13 @@ +//=============================================================================// +// +// Purpose: Interface the engine exposes to the game DLL +// +//=============================================================================// + +#include "core/stdafx.h" +#include "tier0/basetypes.h" +#include "client/IVEngineClient.h" + +//#ifdef GAMEDLL_S3 +bool* m_bRestrictServerCommands = reinterpret_cast(g_mGameDll.StringSearch("DevShotGenerator_Init()").FindPatternSelf("88 05", ADDRESS::Direction::UP).ResolveRelativeAddressSelf(0x2).OffsetSelf(0x2).GetPtr()); +//#endif diff --git a/r5dev/client/IVEngineClient.h b/r5dev/client/IVEngineClient.h new file mode 100644 index 00000000..445b4672 --- /dev/null +++ b/r5dev/client/IVEngineClient.h @@ -0,0 +1,25 @@ +#pragma once + +namespace +{ + /* ==== CVENGINECLIENT ================================================================================================================================================== */ + ADDRESS p_IVEngineClient_CommandExecute = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x57\x48\x83\xEC\x20\x48\x8D\x0D\x27\x61\xa5\x1E\x41\x8B\xD8", "xxxx?xxxxxxxx????xxx"); + void (*IVEngineClient_CommandExecute)(void* self, const char* cmd) = (void (*)(void*, const char*))p_IVEngineClient_CommandExecute.GetPtr(); /*48 89 5C 24 ?? 57 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 41 8B D8*/ +} + +/////////////////////////////////////////////////////////////////////////////// +extern bool* m_bRestrictServerCommands; + +/////////////////////////////////////////////////////////////////////////////// +class HVEngineClient : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: IVEngineClient::CommandExecute : 0x" << std::hex << std::uppercase << p_IVEngineClient_CommandExecute.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: m_bRestrictServerCommands : 0x" << std::hex << std::uppercase << m_bRestrictServerCommands << std::setw(0) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HVEngineClient); \ No newline at end of file diff --git a/r5dev/client/cdll_engine_int.cpp b/r5dev/client/cdll_engine_int.cpp new file mode 100644 index 00000000..bfd36335 --- /dev/null +++ b/r5dev/client/cdll_engine_int.cpp @@ -0,0 +1,133 @@ +#include "core/stdafx.h" +/*****************************************************************************/ +#include "tier0/basetypes.h" +#include "tier0/IConVar.h" +#include "tier0/cvar.h" +#include "client/IVEngineClient.h" +#include "client/client.h" +#include "client/cdll_engine_int.h" +#include "public/include/bansystem.h" +#include "engine/net_chan.h" +#include "vpc/keyvalues.h" +/*****************************************************************************/ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void __fastcall HFrameStageNotify(CHLClient* rcx, ClientFrameStage_t frameStage) +{ + switch (frameStage) + { + case ClientFrameStage_t::FRAME_START: // FrameStageNotify gets called every frame by CEngine::Frame with the stage being FRAME_START. We can use this to check/set global variables. + { + static bool bInitialized = false; + if (!bInitialized) + { + IConVar_ClearHostNames(); + ConCommand_InitConCommand(); + CKeyValueSystem_Init(); + + IVEngineClient_CommandExecute(NULL, "exec autoexec.cfg"); + IVEngineClient_CommandExecute(NULL, "exec autoexec_server.cfg"); + IVEngineClient_CommandExecute(NULL, "exec autoexec_client.cfg"); + + *(bool*)m_bRestrictServerCommands = true; // Restrict commands. + void* disconnect = g_pCvar->FindCommand("disconnect"); + *(std::int32_t*)((std::uintptr_t)disconnect + 0x38) |= FCVAR_SERVER_CAN_EXECUTE; // Make sure server is not restricted to this. + + if (net_userandomkey->m_pParent->m_iValue == 1) + { + HNET_GenerateKey(); + } + g_pCvar->FindVar("net_usesocketsforloopback")->m_pParent->m_iValue = 1; + + bInitialized = true; + } + break; + } + case ClientFrameStage_t::FRAME_NET_UPDATE_POSTDATAUPDATE_END: + { + if (g_pBanSystem->IsRefuseListValid()) + { + for (int i = 0; i < g_pBanSystem->vsvrefuseList.size(); i++) // Loop through vector. + { + for (int c = 0; c < MAX_PLAYERS; c++) // Loop through all possible client instances. + { + CClient* client = g_pClient->GetClientInstance(c); // Get client instance. + if (!client) + { + continue; + } + + if (!client->GetNetChan()) // Netchan valid? + { + continue; + } + + int clientID = g_pClient->m_iUserID + 1; // Get UserID + 1. + if (clientID != g_pBanSystem->vsvrefuseList[i].second) // See if they match. + { + continue; + } + + NET_DisconnectClient(g_pClient, c, g_pBanSystem->vsvrefuseList[i].first.c_str(), 0, 1); + g_pBanSystem->DeleteConnectionRefuse(clientID); + break; + } + } + } + PatchNetVarConVar(); + break; + } + default: + { + break; + } + } + + CHLClient_FrameStageNotify(rcx, (int)frameStage); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void PatchNetVarConVar() +{ + CHAR sConvarPtr[] = "\x72\x3a\x73\x76\x72\x75\x73\x7a\x7a\x03\x04"; + PCHAR curr = sConvarPtr; + while (*curr) + { + *curr ^= 'B'; + ++curr; + } + + std::int64_t nCvarAddr = 0; + std::stringstream ss; + ss << std::hex << std::string(sConvarPtr); + ss >> nCvarAddr; + void* pCvar = reinterpret_cast(nCvarAddr); + + if (*reinterpret_cast(pCvar) == 144) + { + std::uint8_t padding[] = + { + 0x48, 0x8B, 0x45, 0x58, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + void* pCallback = nullptr; + VirtualAlloc(pCallback, 10, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + memcpy(pCallback, (void*)padding, 9); + reinterpret_cast(pCallback)(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +void CHLClient_Attach() +{ + DetourAttach((LPVOID*)&CHLClient_FrameStageNotify, &HFrameStageNotify); +} + +void CHLClient_Detach() +{ + DetourDetach((LPVOID*)&CHLClient_FrameStageNotify, &HFrameStageNotify); +} diff --git a/r5dev/client/cdll_engine_int.h b/r5dev/client/cdll_engine_int.h new file mode 100644 index 00000000..d56bd275 --- /dev/null +++ b/r5dev/client/cdll_engine_int.h @@ -0,0 +1,74 @@ +#pragma once +#include "tier0/basetypes.h" + + +enum class ClientFrameStage_t : int +{ + FRAME_UNDEFINED = -1, // (haven't run any frames yet) + FRAME_START, + + // A network packet is being recieved + FRAME_NET_UPDATE_START, + // Data has been received and we're going to start calling PostDataUpdate + FRAME_NET_UPDATE_POSTDATAUPDATE_START, + // Data has been received and we've called PostDataUpdate on all data recipients + FRAME_NET_UPDATE_POSTDATAUPDATE_END, + // We've received all packets, we can now do interpolation, prediction, etc.. + FRAME_NET_UPDATE_END, + + // We're about to start rendering the scene + FRAME_RENDER_START, + // We've finished rendering the scene. + FRAME_RENDER_END, + + FRAME_NET_FULL_FRAME_UPDATE_ON_REMOVE +}; + +class CHLClient +{ +public: + void FrameStageNotify(ClientFrameStage_t curStage) // @0x1405C0740 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CHLClient*, ClientFrameStage_t); + (*reinterpret_cast(this))[58](this, curStage); /*48 83 EC 28 89 15 ?? ?? ?? ??*/ + } +}; + +namespace +{ + /* ==== CHLCLIENT ======================================================================================================================================================= */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_CHLClient_FrameStageNotify = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x38\x89\x15\x00\x00\x00\x00", "xxxxxx????"); + void (*CHLClient_FrameStageNotify)(void* rcx, int curStage) = (void(*)(void*, int))p_CHLClient_FrameStageNotify.GetPtr(); /*48 83 EC 38 89 15 ?? ?? ?? ??*/ + + ADDRESS p_CHLClient_PostInit = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\x3D\x00\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00", "xxx?????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????xxx????"); + void* (*CHLClient_PostInit)() = (void* (*)())p_CHLClient_PostInit.GetPtr(); /*48 83 3D ? ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 8D 05 ? ? ? ?*/ +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_CHLClient_FrameStageNotify = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x89\x15\x00\x00\x00\x00", "xxxxxx????"); + void (*CHLClient_FrameStageNotify)(void* rcx, int curStage) = (void(*)(void*, int))p_CHLClient_FrameStageNotify.GetPtr(); /*48 83 EC 28 89 15 ?? ?? ?? ??*/ + + ADDRESS p_CHLClient_PostInit = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x48\x83\x3D\x00\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00", "xxxxxxx?????xxx????"); + void* (*CHLClient_PostInit)() = (void* (*)())p_CHLClient_PostInit.GetPtr(); /*48 83 EC 28 48 83 3D ? ? ? ? ? 48 8D 05 ? ? ? ?*/ +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +void __fastcall HFrameStageNotify(CHLClient* rcx, ClientFrameStage_t curStage); +void PatchNetVarConVar(); + +void CHLClient_Attach(); +void CHLClient_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HDll_Engine_Int : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CHLClient::FrameStageNotify : 0x" << std::hex << std::uppercase << p_CHLClient_FrameStageNotify.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: CHLClient::PostInit : 0x" << std::hex << std::uppercase << p_CHLClient_PostInit.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HDll_Engine_Int); diff --git a/r5dev/client/client.cpp b/r5dev/client/client.cpp new file mode 100644 index 00000000..49d3d39e --- /dev/null +++ b/r5dev/client/client.cpp @@ -0,0 +1,5 @@ +#include "core/stdafx.h" +#include "client/client.h" + +/////////////////////////////////////////////////////////////////////////////// +CClient* g_pClient = reinterpret_cast(p_IVEngineServer_PersistenceAvailable.FindPatternSelf("48 8D 0D", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr()); diff --git a/r5dev/client/client.h b/r5dev/client/client.h new file mode 100644 index 00000000..9fb97ec2 --- /dev/null +++ b/r5dev/client/client.h @@ -0,0 +1,69 @@ +#pragma once +#include "tier0/basetypes.h" +#include "server/IVEngineServer.h" + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CClient; + +/////////////////////////////////////////////////////////////////////////////// +extern CClient* g_pClient; + +namespace +{ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + const std::uintptr_t g_dwCClientSize = 0x4A440; + const std::uintptr_t g_dwPersistenceVar = 0x5B4; + const std::uintptr_t g_dwCClientPadding = 303232; +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + const std::uintptr_t g_dwCClientSize = 0x4A4C0; + const std::uintptr_t g_dwPersistenceVar = 0x5BC; + const std::uintptr_t g_dwCClientPadding = 303360; +#endif +} + +class CClient +{ +public: + inline CClient* GetClientInstance(int nIndex) + { + return (CClient*)(std::uintptr_t)(g_pClient + (nIndex * g_dwCClientSize)); + } + + void*& GetNetChan() + { + return m_nNetChannel; + } +private: + char pad_0000[16]; //0x0000 +public: + int m_iUserID; //0x0010 +private: + char pad_0014[908]; //0x0014 +public: + void* m_nNetChannel; //0x03A0 +private: + char pad_03A8[8]; //0x03A8 +public: + int m_iSignonstate; //0x03B0 +private: + char pad_03B4[4]; //0x03B4 +public: + std::int64_t m_iOriginID; //0x03B8 +private: + char pad_03C0[g_dwCClientPadding]; //0x03C0 +}; + +/////////////////////////////////////////////////////////////////////////////// +class HClient : public IDetour +{ + virtual void debugp() + { + std::cout << "| VAR: g_pClient : 0x" << std::hex << std::uppercase << g_pClient << std::setw(0) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HClient); diff --git a/r5dedicated/opcodes.cpp b/r5dev/common/opcodes.cpp similarity index 75% rename from r5dedicated/opcodes.cpp rename to r5dev/common/opcodes.cpp index 32d3239d..77d7a77d 100644 --- a/r5dedicated/opcodes.cpp +++ b/r5dev/common/opcodes.cpp @@ -1,14 +1,12 @@ -#include "pch.h" -#include "hooks.h" -#include "enums.h" -#include "opcodes.h" -#include "gameclasses.h" +#include "core/stdafx.h" +#include "common/opcodes.h" -/*----------------------------------------------------------------------------- - * _opcodes.cpp - *-----------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------- + * _opcodes.cpp + *-----------------------------------------------------------------------------*/ -void Hooks::DedicatedPatch() +#ifdef DEDICATED +void Dedicated_Init() { *(uintptr_t*)0x14D415040 = 0x1417304E8; *(uintptr_t*)0x14B37C3C0 = 0x141F10CA0; @@ -40,10 +38,10 @@ void Hooks::DedicatedPatch() gCEngineAPI__OnStartup.Offset(0x5E).Patch({ 0xE9, 0xC6, 0x01, 0x00, 0x00 }); // JNE --> JNP | Skip Video Mode initialization code. gCEngineAPI__Connect.Offset(0xDD).Patch({ 0x90, 0x90, 0x90 }); // CAL --> NOP | NOP call to texture and material preloading. gCEngineAPI__Connect.Offset(0xF1).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }); // CAL --> NOP | NOP call to texture and material preloading. - gCEngineAPI__Connect.Offset(0x1C6).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90}); // CAL --> NOP | NOP call to texture and material preloading. + gCEngineAPI__Connect.Offset(0x1C6).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); // CAL --> NOP | NOP call to texture and material preloading. //gCEngineAPI__ModInit.Offset(0x3DD).Patch({ 0xE9, 0xB5, 0x00, 0x00, 0x00, 0x00 }); // JNE --> JNP | Skip CreateWindow Initialization code. gCEngineAPI__ModInit.Offset(0x44C).Patch({ 0xEB, 0x49 }); // JNZ --> JMP | Skip CreateGameWindow validation code. - //gCEngineAPI__ModInit.Offset(0x3DD).Patch({ 0xEB, 0x6D }); // JE --> JMP | Skip CreateGameWindow initialization code. + //gCEngineAPI__ModInit.Offset(0x3DD).Patch({ 0xEB, 0x6D }); // JE --> JMP | Skip CreateGameWindow initialization code. //------------------------------------------------------------------------- // CENGINEVGUI @@ -59,7 +57,7 @@ void Hooks::DedicatedPatch() //------------------------------------------------------------------------- // CHLClIENT //------------------------------------------------------------------------- - gCHLClient__1000.Patch({ 0xC3 }); // FUN --> RET | Return early in 'gCHLClient::unnamed' to prevent infinite loop. + gCHLClient__1000.Patch({ 0xC3 }); // FUN --> RET | Return early in 'gCHLClient::unnamed' to prevent infinite loop. gCHLClient__HudMessage.Patch({ 0xC3 }); // FUN --> RET | Return early from 'CHudMessage' call. //------------------------------------------------------------------------- @@ -82,7 +80,7 @@ void Hooks::DedicatedPatch() //------------------------------------------------------------------------- // CSHADERSYSTEM //------------------------------------------------------------------------- - //gCShaderSystem_Init.Patch({ 0xC3 }); // FUN --> RET | Return early in 'CShaderSystem::Init' to prevent initialization. + //gCShaderSystem_Init.Patch({ 0xC3 }); // FUN --> RET | Return early in 'CShaderSystem::Init' to prevent initialization. gCShaderSystem__9.Offset(0x3).Patch({ 0xE9, 0x95, 0x03, 0x00, 0x00 }); // Unnecessary CShaderSystem call? //------------------------------------------------------------------------- @@ -102,7 +100,7 @@ void Hooks::DedicatedPatch() gHost_Init_1.Offset(0x621).Patch({ 0xEB, 0x0C }); // JNE --> JMP | Skip client.dll Init_PostVideo() validation code. gHost_Init_1.Offset(0x658).Patch({ 0xE9, 0x8C, 0x00, 0x00, 0x00 }); // JE --> JMP | Skip NULL call as client is never initialized. gHost_Init_1.Offset(0x6E9).Patch({ 0xE9, 0xB0, 0x00, 0x00, 0x00 }); // JNE --> JMP | Skip shader preloading as cvar can't be checked due to client being NULL. - //gHost_Init_2.Offset(0x5D8).Patch({ 0xEB, 0x05 }); // JE --> JMP | Render? + //gHost_Init_2.Offset(0x5D8).Patch({ 0xEB, 0x05 }); // JE --> JMP | Render? //------------------------------------------------------------------------- // RUNTIME: _HOST_RUNFRAME @@ -110,6 +108,11 @@ void Hooks::DedicatedPatch() //s1.Offset(0x1C6).Patch({ 0xE9, 0xAD, 0x11, 0x00, 0x00 }); // JNE --> JMP | Return early in _Host_RunFrame() for debugging perposes. //s1.Offset(0x1010).Patch({ 0xEB, 0x14 }); // JNE --> JMP | Return early in _Host_RunFrame() for debugging perposes. + //------------------------------------------------------------------------- + // RUNTIME: HOST_NEWGAME + //------------------------------------------------------------------------- + Host_NewGame.Offset(0x637).Patch({ 0xE9, 0xC1, 0x00, 0x00, 0x00 }); // JNE --> JMP | Prevent connect localhost from being executed in Host_NewGame. + //------------------------------------------------------------------------- // RUNTIME: EBISUSDK //------------------------------------------------------------------------- @@ -163,35 +166,92 @@ void Hooks::DedicatedPatch() gShaderDispatch.Offset(0x62).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); // CAL --> NOP | Prevent memory allocation and population for shader assets. // UNKNOWN ---------------------------------------------------------------- - MemoryAddress t8 = 0x00000001403C0480; + ADDRESS t8 = 0x00000001403C0480; t8.Patch({ 0xC3 }); // Return from unknown call during ChangeLevel. [LATE] - MemoryAddress t9 = 0x00000001403EE420; + ADDRESS t9 = 0x00000001403EE420; t9.Patch({ 0xC3 }); // Return from unknown call during ChangeLevel. [EARLY] //------------------------------------------------------------------------- // RUNTIME BLOCK //------------------------------------------------------------------------- - MemoryAddress t0 = 0x00000001401D71E0; + ADDRESS t0 = 0x00000001401D71E0; t0.Patch({ 0xC3 }); - MemoryAddress t1 = 0x0000000140456B50; + ADDRESS t1 = 0x0000000140456B50; t1.Offset(0x292).Patch({ 0xE9, 0xEE, 0x00, 0x00, 0x00 }); - MemoryAddress t2 = 0x0000000140238DA0; + ADDRESS t2 = 0x0000000140238DA0; t2.Offset(0x4E0).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); - MemoryAddress t3 = 0x0000000140312D80; + ADDRESS t3 = 0x0000000140312D80; //t3.Offset(0xB3).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); - MemoryAddress t4 = 0x0000000140312D80; // Patch Additional shader preloading. + ADDRESS t4 = 0x0000000140312D80; // Patch Additional shader preloading. //t4.Offset(0xB3).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); - MemoryAddress t5 = 0x00000001403BBFD0; + ADDRESS t5 = 0x00000001403BBFD0; t5.Offset(0x7D8).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); //------------------------------------------------------------------------- // END RUNTIME BLOCK //------------------------------------------------------------------------- } +#endif // DEDICATED -// TEST -void SetCHostState() +void RuntimePtc_Init() /* .TEXT */ { - static std::string ServerMap = std::string(); - ServerMap = "mp_rr_canyonlands_64k_x_64k"; - strncpy_s(GameGlobals::HostState->m_levelName, ServerMap.c_str(), 64); // Copy new map into hoststate levelname. 64 is size of m_levelname. - GameGlobals::HostState->m_iNextState = HostStates_t::HS_NEW_GAME; // Force CHostState::FrameUpdate to start a server. -} \ No newline at end of file +#ifdef DEDICATED + //------------------------------------------------------------------------- + // JNZ --> JMP | Prevent OriginSDK from initializing on the server + Origin_Init.Offset(0x0B).Patch({ 0xE9, 0x63, 0x02, 0x00, 0x00, 0x00 }); + Origin_SetState.Offset(0x0E).Patch({ 0xE9, 0xCB, 0x03, 0x00, 0x00, 0x00 }); +#endif // DEDICATED + //------------------------------------------------------------------------- + // JNE --> JMP | Allow games to be loaded without the optional texture streaming file + //WriteProcessMemory(GameProcess, LPVOID(dst002 + 0x8E5), "\xEB\x19", 2, NULL); + //------------------------------------------------------------------------- + // JNE --> JMP | Prevent connect command from crashing by invalid call to UI function + dst003.Offset(0x1D6).Patch({ 0xEB, 0x27 }); + //------------------------------------------------------------------------- + // JA --> JMP | Prevent FairFight anti-cheat from initializing on the + FairFight_Init.Offset(0x61).Patch({ 0xE9, 0xED, 0x00, 0x00, 0x00, 0x00 }); +} + +void RuntimePtc_Toggle() /* .TEXT */ +{ + static bool g_nop = true; + + if (g_nop) + { + //------------------------------------------------------------------------- + // CALL --> NOP | Allow some maps to be loaded by nopping out a call in LoadProp function + //WriteProcessMemory(GameProcess, LPVOID(dst007 + 0x5E8), "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90", 11, NULL); + + dst007.Offset(0x5E8).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }); + + + //------------------------------------------------------------------------- + // CALL --> NOP | Disable the viewmodel rendered to avoid a crash from a certain entity in desertlands_mu1 + //WriteProcessMemory(GameProcess, LPVOID(dst008 + 0x67), "\x90\x90\x90\x90\x90", 5, NULL); + dst008.Offset(0x67).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); + + + printf("\n"); + printf("+--------------------------------------------------------+\n"); + printf("|>>>>>>>>>>>>>>| TEXT OPCODES OVERWRITTEN |<<<<<<<<<<<<<<|\n"); + printf("+--------------------------------------------------------+\n"); + printf("\n"); + } + else + { + //------------------------------------------------------------------------- + // NOP --> CALL | Recover function DST007 + //WriteProcessMemory(GameProcess, LPVOID(dst007 + 0x5E8), "\x48\x8B\x03\xFF\x90\xB0\x02\x00\x00\x84\xC0", 11, NULL); + + dst007.Offset(0x5E8).Patch({ 0x48, 0x8B, 0x03, 0xFF, 0x90, 0xB0, 0x02, 0x00, 0x00, 0x84, 0xC0 }); + //------------------------------------------------------------------------- + // NOP --> CALL | Recover function DST008 + //WriteProcessMemory(GameProcess, LPVOID(dst008 + 0x67), "\xE8\x54\xD8\xFF\xFF", 5, NULL); + dst008.Offset(0x67).Patch({ 0xE8, 0x54, 0xD8, 0xFF, 0xFF }); + + printf("\n"); + printf("+--------------------------------------------------------+\n"); + printf("|>>>>>>>>>>>>>>>| TEXT OPCODES RECOVERED |<<<<<<<<<<<<<<<|\n"); + printf("+--------------------------------------------------------+\n"); + printf("\n"); + } + g_nop = !g_nop; +} diff --git a/r5dev/common/opcodes.h b/r5dev/common/opcodes.h new file mode 100644 index 00000000..538102d9 --- /dev/null +++ b/r5dev/common/opcodes.h @@ -0,0 +1,119 @@ +#pragma once +#include +#include +#include "public/include/utility.h" + +void Dedicated_Init(); +void RuntimePtc_Init(); +void RuntimePtc_Toggle(); + +namespace +{ + ///* -------------- ORIGIN ------------------------------------------------------------------------------------------------------------------------------------------------ */ + ADDRESS Origin_Init = /*0x14032EEA0*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x83\xEC\x28\x80\x3D\x00\x00\x00\x23\x00\x0F\x85\x00\x02\x00", "xxxxxx???xxxx?xx"); + ADDRESS Origin_SetState = /*0x140330290*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x81\xEC\x58\x04\x00\x00\x80\x3D\x00\x00\x00\x00\x00\x0F\x84", "xxxxxxxxx????xxx"); + + ///* -------------- ENGINE ------------------------------------------------------------------------------------------------------------------------------------------------ */ + //ADDRESS dst002 = /*0x14043FB90*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x89\x4C\x24\x08\x56\x41\x55\x48\x81\xEC\x68\x03\x00\x00\x4C", "xxxx?xxxxxxxxxxx"); + ADDRESS dst003 = /*0x14022A4A0*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x83\xEC\x38\x0F\x29\x74\x24\x20\x48\x89\x5C\x24\x40\x48\x8B", "xxxxxxxxxxxxxxxx"); + ADDRESS Host_NewGame = /*0x140238DA0*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x8B\xC4\x00\x41\x54\x41\x00\x48\x81\xEC\x00\x00\x00\x00\xF2", "xxx?xxx?xxx??xxx"); + + ///* -------------- NETCHAN ----------------------------------------------------------------------------------------------------------------------------------------------- */ + //ADDRESS CServer_Auth = /*0x14030D000*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x40\x55\x57\x41\x55\x41\x57\x48\x8D\xAC\x24\x28\xFF\xFF\xFF\x48", "xxxxxxxxxxxxxxxx"); + + ///* -------------- FAIRFIGHT --------------------------------------------------------------------------------------------------------------------------------------------- */ + ADDRESS FairFight_Init = /*0x140303AE0*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x40\x53\x48\x83\xEC\x20\x8B\x81\xB0\x03\x00\x00\x48\x8B\xD9\xC6", "xxxxxxxxxxxxxxxx"); + + ///* -------------- OTHER ------------------------------------------------------------------------------------------------------------------------------------------------- */ + ADDRESS dst007 = /*0x14028F3B0*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x8B\xC4\x44\x89\x40\x18\x48\x89\x50\x10\x55\x53\x56\x57\x41", "xxxxxxxxxxxxxxxx"); + ADDRESS dst008 = /*0x140E3E110*/ FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x83\xEC\x78\x48\x8B\x84\x24\x00\x00\x00\x00\x4D\x8B\xD8\x00", "xxxxxxxx????xxx?"); + + //ADDRESS dst009 = FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x8B\xC4\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8B\xEC\x48\x83\xEC\x60", "xxxxxxxxxxxxxxxxxxx"); + + ///* -------------- ------- ----------------------------------------------------------------------------------------------------------------------------------------------- */ + + namespace + { + // TODO: create patterns instead and rename to function names. + // Renderer + ADDRESS r0 = 0x00000001402FE280; // + ADDRESS gCShaderGlue__Init = 0x00000001403B3A50; // + ADDRESS gMatSync = 0x00000001403DEE90; // + ADDRESS gCMaterialSystem__MatsysMode_Init = 0x00000001403BD120; // + ADDRESS r4 = 0x0000000140404380; // + ADDRESS r5 = 0x000000014040D850; // + ADDRESS r6 = 0x0000000140413260; // + ADDRESS r7 = 0x00000001404093F0; // + ADDRESS r8 = 0x00000001403D2E60; // + ADDRESS d3d11init = 0x000000014043CDF0; // + + // Engine + ADDRESS gHost_Init_0 = 0x0000000140236E40; // main Host_Init()? + ADDRESS e1 = 0x0000000140FB2F10; // also used by CServerGameDLL + ADDRESS addr_CEngine_Frame = 0x00000001402970E0; + ADDRESS e3 = 0x0000000140231C00; + ADDRESS e4 = 0x0000000140BE1970; + ADDRESS e5 = 0x0000000140DBBAF0; + ADDRESS e6 = 0x0000000140DBE610; + ADDRESS e7 = 0x000000014044AFA0; + ADDRESS e8 = 0x000000014027EC50; // RenderFrame? + ADDRESS gCEngineAPI__Init = 0x0000000140342FB0; // + ADDRESS gCEngineAPI__ModInit = 0x0000000140343DE0; // + ADDRESS gCEngineAPI__Connect = 0x0000000140342BA0; // + ADDRESS gCEngineAPI__OnStartup = 0x0000000140343860; // + ADDRESS gCSourceAppSystemGroup__Create = 0x000000014044AFA0; // + ADDRESS gCShaderSystem__Init = 0x00000001403DF870; // + ADDRESS gInitMaterialSystem = 0x000000014024B390; // + ADDRESS gCVideoMode_Common__DrawStartupGraphic = 0x000000014027F0F0; // + ADDRESS gShaderDispatch = 0x00000001403EE5C0; + ADDRESS gShaderCreate = 0x00000001403ECD00; // + ADDRESS gTextureCreate = 0x00000001403EDCD0; + + ADDRESS gCShaderSystem__9 = 0x00000001403DFC30; + ADDRESS gBSP_LUMP_INIT = 0x00000001402546F0; // BSP. + + + ADDRESS e9 = 0x00000001404066E0; + ADDRESS e10 = 0x00000001403B49E0; // CMaterialGlue? + + // SERVER + ADDRESS gHost_Init_1 = 0x0000000140237B00; // server Host_Init()? + ADDRESS s1 = 0x0000000140231C00; // _Host_RunFrame() with inlined CFrameTimer::MarkFrame()? + ADDRESS s2 = 0x00000001402312A0; // server HeartBeat? (baseserver.cpp) + ADDRESS s3 = 0x0000000140FB36D0; // TEMP?? + + // CLIENT + ADDRESS gHost_Init_2 = 0x0000000140236640; // client Host_Init()? + ADDRESS gCGame__CreateGameWindow = 0x0000000140299100; // + ADDRESS c2 = 0x00000001403F4360; // 1403DF870 --> 1403F4360 + ADDRESS c3 = 0x00000001403F8A80; // 1403DF870 --> 1403F8A40 + ADDRESS gCHLClient__1000 = 0x00000001405C27B0; // CHLClient + 1000 + ADDRESS gCHLClient__HudMessage = 0x00000001405BAC00; // CHudMessage + ADDRESS c6 = 0x00000001403CA2D0; // + + // VGUI + ADDRESS gCEngineVGui__Init = 0x0000000140282E40; // jumptable + ADDRESS gCEngineVGui__OnLevelLoadingStarted = 0x00000001402830D0; + ADDRESS SCR_BeginLoadingPlaque = 0x000000014023E870; + } + + + void PrintOAddress() // Test the sigscan results + { + std::cout << "+--------------------------------------------------------+" << std::endl; + std::cout << "| Origin_Init : " << std::hex << std::uppercase << Origin_Init.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "| Origin_SetState : " << std::hex << std::uppercase << Origin_SetState.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "+--------------------------------------------------------+" << std::endl; + //std::cout << "| dst002 : " << std::hex << std::uppercase << dst002.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "| dst003 : " << std::hex << std::uppercase << dst003.GetPtr() << std::setw(20) << " |" << std::endl; + //std::cout << "| Host_NewGame : " << std::hex << std::uppercase << Host_NewGame.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "+--------------------------------------------------------+" << std::endl; + //std::cout << "| CServer_Auth : " << std::hex << std::uppercase << CServer_Auth.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "+--------------------------------------------------------+" << std::endl; + std::cout << "| FairFight_Init : " << std::hex << std::uppercase << FairFight_Init.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "+--------------------------------------------------------+" << std::endl; + std::cout << "| dst007 : " << std::hex << std::uppercase << dst007.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "| dst008 : " << std::hex << std::uppercase << dst008.GetPtr() << std::setw(20) << " |" << std::endl; + std::cout << "+--------------------------------------------------------+" << std::endl; + } +} diff --git a/r5dev/common/protocol.h b/r5dev/common/protocol.h new file mode 100644 index 00000000..44c94d9d --- /dev/null +++ b/r5dev/common/protocol.h @@ -0,0 +1,19 @@ +#pragma once + +/*----------------------------------------------------------------------------- + * _protocol.h + *-----------------------------------------------------------------------------*/ + +enum class SIGNONSTATE : int +{ + SIGNONSTATE_NONE = 0, // no state yet; about to connect. + SIGNONSTATE_CHALLENGE = 1, // client challenging server; all OOB packets. + SIGNONSTATE_CONNECTED = 2, // client is connected to server; netchans ready. + SIGNONSTATE_NEW = 3, // just got serverinfo and string tables. + SIGNONSTATE_PRESPAWN = 4, // received signon buffers. + SIGNONSTATE_GETTING_DATA = 5, // getting persistence data. + SIGNONSTATE_SPAWN = 6, // ready to receive entity packets. + SIGNONSTATE_FIRST_SNAP = 7, // received baseline snapshot. + SIGNONSTATE_FULL = 8, // we are fully connected; first non-delta packet received. + SIGNONSTATE_CHANGELEVEL = 9, // server is changing level; please wait. +}; diff --git a/r5dev/common/psuedodefs.h b/r5dev/common/psuedodefs.h new file mode 100644 index 00000000..0a7a19f3 --- /dev/null +++ b/r5dev/common/psuedodefs.h @@ -0,0 +1,462 @@ +/* + + This file contains definitions used in the Hex-Rays decompiler output. + It has type definitions and convenience macros to make the + output more readable. + + Copyright (c) 2007-2020 Hex-Rays + +*/ + +#ifndef HEXRAYS_DEFS_H +#define HEXRAYS_DEFS_H + +#if defined(__GNUC__) + typedef long long ll; + typedef unsigned long long ull; + #define __int64 long long + #define __int32 int + #define __int16 short + #define __int8 char + #define MAKELL(num) num ## LL + #define FMT_64 "ll" +#elif defined(_MSC_VER) + typedef __int64 ll; + typedef unsigned __int64 ull; + #define MAKELL(num) num ## i64 + #define FMT_64 "I64" +#elif defined (__BORLANDC__) + typedef __int64 ll; + typedef unsigned __int64 ull; + #define MAKELL(num) num ## i64 + #define FMT_64 "L" +#else + #error "unknown compiler" +#endif +typedef unsigned int uint; +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned long ulong; + +typedef char int8; +typedef signed char sint8; +typedef unsigned char uint8; +typedef short int16; +typedef signed short sint16; +typedef unsigned short uint16; +typedef int int32; +typedef signed int sint32; +typedef unsigned int uint32; +typedef ll int64; +typedef ll sint64; +typedef ull uint64; + +// Partially defined types. They are used when the decompiler does not know +// anything about the type except its size. +#define _BYTE uint8 +#define _WORD uint16 +#define _DWORD uint32 +#define _QWORD uint64 +#if !defined(_MSC_VER) +#define _LONGLONG __int128 +#endif + +// Non-standard boolean types. They are used when the decompiler cannot use +// the standard "bool" type because of the size mistmatch but the possible +// values are only 0 and 1. See also 'BOOL' type below. +typedef int8 _BOOL1; +typedef int16 _BOOL2; +typedef int32 _BOOL4; +typedef int64 _BOOL8; + +#ifndef _WINDOWS_ +typedef int8 BYTE; +typedef int16 WORD; +typedef int32 DWORD; +typedef int32 LONG; +typedef int BOOL; // uppercase BOOL is usually 4 bytes +#endif +typedef int64 QWORD; +#ifndef __cplusplus +typedef int bool; // we want to use bool in our C programs +#endif + +#define __pure // pure function: + // when given the same arguments, always returns the same value + // has no side effects + +// Non-returning function +#if defined(__GNUC__) +#define __noreturn __attribute__((noreturn)) +#else +#define __noreturn __declspec(noreturn) +#endif + + +#ifndef NULL +#define NULL 0 +#endif + +// Some convenience macros to make partial accesses nicer +#define LAST_IND(x,part_type) (sizeof(x)/sizeof(part_type) - 1) +#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN +# define LOW_IND(x,part_type) LAST_IND(x,part_type) +# define HIGH_IND(x,part_type) 0 +#else +# define HIGH_IND(x,part_type) LAST_IND(x,part_type) +# define LOW_IND(x,part_type) 0 +#endif +// first unsigned macros: +#define BYTEn(x, n) (*((_BYTE*)&(x)+n)) +#define WORDn(x, n) (*((_WORD*)&(x)+n)) +#define DWORDn(x, n) (*((_DWORD*)&(x)+n)) + +#define LOBYTE(x) BYTEn(x,LOW_IND(x,_BYTE)) +#define LOWORD(x) WORDn(x,LOW_IND(x,_WORD)) +#define LODWORD(x) DWORDn(x,LOW_IND(x,_DWORD)) +#define HIBYTE(x) BYTEn(x,HIGH_IND(x,_BYTE)) +#define HIWORD(x) WORDn(x,HIGH_IND(x,_WORD)) +#define HIDWORD(x) DWORDn(x,HIGH_IND(x,_DWORD)) +#define BYTE1(x) BYTEn(x, 1) // byte 1 (counting from 0) +#define BYTE2(x) BYTEn(x, 2) +#define BYTE3(x) BYTEn(x, 3) +#define BYTE4(x) BYTEn(x, 4) +#define BYTE5(x) BYTEn(x, 5) +#define BYTE6(x) BYTEn(x, 6) +#define BYTE7(x) BYTEn(x, 7) +#define BYTE8(x) BYTEn(x, 8) +#define BYTE9(x) BYTEn(x, 9) +#define BYTE10(x) BYTEn(x, 10) +#define BYTE11(x) BYTEn(x, 11) +#define BYTE12(x) BYTEn(x, 12) +#define BYTE13(x) BYTEn(x, 13) +#define BYTE14(x) BYTEn(x, 14) +#define BYTE15(x) BYTEn(x, 15) +#define WORD1(x) WORDn(x, 1) +#define WORD2(x) WORDn(x, 2) // third word of the object, unsigned +#define WORD3(x) WORDn(x, 3) +#define WORD4(x) WORDn(x, 4) +#define WORD5(x) WORDn(x, 5) +#define WORD6(x) WORDn(x, 6) +#define WORD7(x) WORDn(x, 7) + +// now signed macros (the same but with sign extension) +#define SBYTEn(x, n) (*((int8*)&(x)+n)) +#define SWORDn(x, n) (*((int16*)&(x)+n)) +#define SDWORDn(x, n) (*((int32*)&(x)+n)) + +#define SLOBYTE(x) SBYTEn(x,LOW_IND(x,int8)) +#define SLOWORD(x) SWORDn(x,LOW_IND(x,int16)) +#define SLODWORD(x) SDWORDn(x,LOW_IND(x,int32)) +#define SHIBYTE(x) SBYTEn(x,HIGH_IND(x,int8)) +#define SHIWORD(x) SWORDn(x,HIGH_IND(x,int16)) +#define SHIDWORD(x) SDWORDn(x,HIGH_IND(x,int32)) +#define SBYTE1(x) SBYTEn(x, 1) +#define SBYTE2(x) SBYTEn(x, 2) +#define SBYTE3(x) SBYTEn(x, 3) +#define SBYTE4(x) SBYTEn(x, 4) +#define SBYTE5(x) SBYTEn(x, 5) +#define SBYTE6(x) SBYTEn(x, 6) +#define SBYTE7(x) SBYTEn(x, 7) +#define SBYTE8(x) SBYTEn(x, 8) +#define SBYTE9(x) SBYTEn(x, 9) +#define SBYTE10(x) SBYTEn(x, 10) +#define SBYTE11(x) SBYTEn(x, 11) +#define SBYTE12(x) SBYTEn(x, 12) +#define SBYTE13(x) SBYTEn(x, 13) +#define SBYTE14(x) SBYTEn(x, 14) +#define SBYTE15(x) SBYTEn(x, 15) +#define SWORD1(x) SWORDn(x, 1) +#define SWORD2(x) SWORDn(x, 2) +#define SWORD3(x) SWORDn(x, 3) +#define SWORD4(x) SWORDn(x, 4) +#define SWORD5(x) SWORDn(x, 5) +#define SWORD6(x) SWORDn(x, 6) +#define SWORD7(x) SWORDn(x, 7) + +// Generate a pair of operands. S stands for 'signed' +#define __SPAIR16__(high, low) (((int16) (high) << 8) | (uint8) (low)) +#define __SPAIR32__(high, low) (((int32) (high) << 16) | (uint16)(low)) +#define __SPAIR64__(high, low) (((int64) (high) << 32) | (uint32)(low)) +#define __SPAIR128__(high, low) (((int128) (high) << 64) | (uint64)(low)) +#define __PAIR16__(high, low) (((uint16) (high) << 8) | (uint8) (low)) +#define __PAIR32__(high, low) (((uint32) (high) << 16) | (uint16)(low)) +#define __PAIR64__(high, low) (((uint64) (high) << 32) | (uint32)(low)) +#define __PAIR128__(high, low) (((uint128)(high) << 64) | (uint64)(low)) + +// Helper functions to represent some assembly instructions. + +#ifdef __cplusplus + +// compile time assertion +#define __CASSERT_N0__(l) COMPILE_TIME_ASSERT_ ## l +#define __CASSERT_N1__(l) __CASSERT_N0__(l) +#define CASSERT(cnd) typedef char __CASSERT_N1__(__LINE__) [(cnd) ? 1 : -1] + +// check that unsigned multiplication does not overflow +template bool is_mul_ok(T count, T elsize) +{ + CASSERT(T(-1) > 0); // make sure T is unsigned + if ( elsize == 0 || count == 0 ) + return true; + return count <= T(-1) / elsize; +} + +// multiplication that saturates (yields the biggest value) instead of overflowing +// such a construct is useful in "operator new[]" +template bool saturated_mul(T count, T elsize) +{ + return is_mul_ok(count, elsize) ? count * elsize : T(-1); +} + +#include // for size_t + +// memcpy() with determined behavoir: it always copies +// from the start to the end of the buffer +// note: it copies byte by byte, so it is not equivalent to, for example, rep movsd +inline void *qmemcpy(void *dst, const void *src, size_t cnt) +{ + char *out = (char *)dst; + const char *in = (const char *)src; + while ( cnt > 0 ) + { + *out++ = *in++; + --cnt; + } + return dst; +} + +// rotate left +template T __ROL__(T value, int count) +{ + const uint nbits = sizeof(T) * 8; + + if ( count > 0 ) + { + count %= nbits; + T high = value >> (nbits - count); + if ( T(-1) < 0 ) // signed value + high &= ~((T(-1) << count)); + value <<= count; + value |= high; + } + else + { + count = -count % nbits; + T low = value << (nbits - count); + value >>= count; + value |= low; + } + return value; +} + +inline uint8 __ROL1__(uint8 value, int count) { return __ROL__((uint8)value, count); } +inline uint16 __ROL2__(uint16 value, int count) { return __ROL__((uint16)value, count); } +inline uint32 __ROL4__(uint32 value, int count) { return __ROL__((uint32)value, count); } +inline uint64 __ROL8__(uint64 value, int count) { return __ROL__((uint64)value, count); } +inline uint8 __ROR1__(uint8 value, int count) { return __ROL__((uint8)value, -count); } +inline uint16 __ROR2__(uint16 value, int count) { return __ROL__((uint16)value, -count); } +inline uint32 __ROR4__(uint32 value, int count) { return __ROL__((uint32)value, -count); } +inline uint64 __ROR8__(uint64 value, int count) { return __ROL__((uint64)value, -count); } + +// the carry flag of a left shift +template int8 __MKCSHL__(T value, uint count) +{ + const uint nbits = sizeof(T) * 8; + count %= nbits; + + return (value >> (nbits-count)) & 1; +} + +// the carry flag of a right shift +template int8 __MKCSHR__(T value, uint count) +{ + return (value >> (count-1)) & 1; +} + +// sign flag +template int8 __SETS__(T x) +{ + if ( sizeof(T) == 1 ) + return int8(x) < 0; + if ( sizeof(T) == 2 ) + return int16(x) < 0; + if ( sizeof(T) == 4 ) + return int32(x) < 0; + return int64(x) < 0; +} + +// overflow flag of subtraction (x-y) +template int8 __OFSUB__(T x, U y) +{ + if ( sizeof(T) < sizeof(U) ) + { + U x2 = x; + int8 sx = __SETS__(x2); + return (sx ^ __SETS__(y)) & (sx ^ __SETS__(U(x2-y))); + } + else + { + T y2 = y; + int8 sx = __SETS__(x); + return (sx ^ __SETS__(y2)) & (sx ^ __SETS__(T(x-y2))); + } +} + +// overflow flag of addition (x+y) +template int8 __OFADD__(T x, U y) +{ + if ( sizeof(T) < sizeof(U) ) + { + U x2 = x; + int8 sx = __SETS__(x2); + return ((1 ^ sx) ^ __SETS__(y)) & (sx ^ __SETS__(U(x2+y))); + } + else + { + T y2 = y; + int8 sx = __SETS__(x); + return ((1 ^ sx) ^ __SETS__(y2)) & (sx ^ __SETS__(T(x+y2))); + } +} + +// https://en.wikipedia.org/wiki/Carry_flag#Carry_flag_vs._borrow_flag +#if defined(__ARM__) || defined(__PPC__) +#define SUB_WITH_CARRY 1 +#else +#define SUB_WITH_CARRY 0 +#endif + +// carry flag of subtraction (x-y) +template int8 __CFSUB__(T x, U y) +{ + int size = sizeof(T) > sizeof(U) ? sizeof(T) : sizeof(U); + bool res; + if ( size == 1 ) + res = uint8(x) < uint8(y); + else if ( size == 2 ) + res = uint16(x) < uint16(y); + else if ( size == 4 ) + res = uint32(x) < uint32(y); + else + res = uint64(x) < uint64(y); +#if SUB_WITH_CARRY + res = !res; +#endif + return res; +} + +// carry flag of addition (x+y) +template int8 __CFADD__(T x, U y) +{ + int size = sizeof(T) > sizeof(U) ? sizeof(T) : sizeof(U); + if ( size == 1 ) + return uint8(x) > uint8(x+y); + if ( size == 2 ) + return uint16(x) > uint16(x+y); + if ( size == 4 ) + return uint32(x) > uint32(x+y); + return uint64(x) > uint64(x+y); +} + +// carry flag of subtraction with carry +template int8 __CFSUB__(T x, U y, int8 cf) +{ +#if SUB_WITH_CARRY + cf = !cf; +#endif + return __CFADD__(y, cf) ^ __CFSUB(x, y + cf); +} + +// overflow flag of subtraction with carry +template int8 __OFSUB__(T x, U y, int8 cf) +{ +#if SUB_WITH_CARRY + cf = !cf; +#endif + return __OFADD__(y, cf) ^ __OFSUB(x, y + cf); +} + +inline uint8 abs8(int8 x) { return x >= 0 ? x : -x; } +inline uint16 abs16(int16 x) { return x >= 0 ? x : -x; } +inline uint32 abs32(int32 x) { return x >= 0 ? x : -x; } +inline uint64 abs64(int64 x) { return x >= 0 ? x : -x; } +//inline uint128 abs128(int128 x) { return x >= 0 ? x : -x; } + +#include // for memcpy +#include // for enable_if + +template +inline typename std::enable_if::type __coerce(F f) +{ + T t; + memcpy(&t, &f, sizeof(T)); + return t; +} +#define COERCE_FLOAT(v) __coerce(v) +#define COERCE_DOUBLE(v) __coerce(v) +#define COERCE_LONG_DOUBLE(v) __coerce(v) +#define COERCE_UNSIGNED_INT(v) __coerce(v) +#define COERCE_UNSIGNED_INT64(v) __coerce(v) + +#else // C++ +// For C, we just provide macros, they are not quite correct. +#define __ROL__(x, y) __rotl__(x, y) // Rotate left +#define __ROR__(x, y) __rotr__(x, y) // Rotate right +#define __CFSHL__(x, y) invalid_operation // Generate carry flag for (x<>y) +#define __CFADD__(x, y) invalid_operation // Generate carry flag for (x+y) +#define __CFSUB__(x, y) invalid_operation // Generate carry flag for (x-y) +#define __OFADD__(x, y) invalid_operation // Generate overflow flag for (x+y) +#define __OFSUB__(x, y) invalid_operation // Generate overflow flag for (x-y) + +#define abs8(x) (int8) ((int8) (x) >= 0 ? (x) : -(x)) +#define abs16(x) (int16) ((int16) (x) >= 0 ? (x) : -(x)) +#define abs32(x) (int32) ((int32) (x) >= 0 ? (x) : -(x)) +#define abs64(x) (int64) ((int64) (x) >= 0 ? (x) : -(x)) +#define abs128(x) (int128)((int128)(x) >= 0 ? (x) : -(x)) + +#endif // C++ + +#if defined(__MIPS__) +// traps for MIPS arithmetic operation +void __noreturn __integer_oveflow(void); // SIGFPE/FPE_INTOVF +void __noreturn __divide_by_zero(void); // SIGFPE/FPE_INTDIV +void __noreturn __trap(uint16 trapcode); // SIGTRAP +void __noreturn __break(uint16 code, uint16 subcode); +#endif + +// No definition for rcl/rcr because the carry flag is unknown +#define __RCL__(x, y) invalid_operation // Rotate left thru carry +#define __RCR__(x, y) invalid_operation // Rotate right thru carry +#define __MKCRCL__(x, y) invalid_operation // Generate carry flag for a RCL +#define __MKCRCR__(x, y) invalid_operation // Generate carry flag for a RCR +#define __SETP__(x, y) invalid_operation // Generate parity flag for (x-y) + +// In the decompilation listing there are some objects declared as _UNKNOWN +// because we could not determine their types. Since the C compiler does not +// accept void item declarations, we replace them by anything of our choice, +// for example a char: + +#define _UNKNOWN char + +#ifdef _MSC_VER +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#endif + +// The ADJ() macro is used for shifted pointers. +// While compilers do not understand it, it makes the code more readable. +// A shifted pointer is declared like this, for example: +// char *__shifted(mystruct,8) p; +// It means: while 'p' points to 'char', it also points to the middle of 'mystruct'. +// More precisely, it is at the offset of 8 bytes from the beginning of 'mystruct'. +// +// The ADJ() macro performs the necessary adjustment. +// The __parentof() and __deltaof() functions are made up, they do not exist. +// __parentof() returns the parent structure type. +// __deltaof() returns the shift amount. + +#define ADJ(p) (__parentof(p) *)(p-__deltaof(p)) + +#endif // HEXRAYS_DEFS_H diff --git a/r5dev/src/dllmain.cpp b/r5dev/core/dllmain.cpp similarity index 52% rename from r5dev/src/dllmain.cpp rename to r5dev/core/dllmain.cpp index 2e25f83c..fd6f8e8e 100644 --- a/r5dev/src/dllmain.cpp +++ b/r5dev/core/dllmain.cpp @@ -1,34 +1,43 @@ -#include "pch.h" -#include "r5dev.h" -#include "id3dx.h" -#include "input.h" -#include "hooks.h" -#include "opcptc.h" -#include "console.h" +#include "core/stdafx.h" +#include "core/r5dev.h" +#include "core/init.h" +/*****************************************************************************/ +#ifndef DEDICATED +#include "windows/id3dx.h" +#include "windows/input.h" +#endif // !DEDICATED +#include "windows/console.h" //############################################################################# // INITIALIZATION //############################################################################# -void InitializeR5Dev() +void R5Dev_Init() { - SetupConsole(); - Hooks::InstallHooks(); - InstallOpcodes(); - g_GuiConfig.Load(); // Load gui config. - SetupDXSwapChain(); + Console_Init(); + Systems_Init(); + +#ifndef DEDICATED + Input_Init(); + DirectX_Init(); +#endif // !DEDICATED spdlog::get("console")->set_pattern("%v"); spdlog::info("+-----------------------------------------------------------------------------+\n"); - spdlog::info("| R5 DEV -- INITIALIZED ------------------------------------------------- |\n"); + spdlog::info("| R5 DEVELOPER CONSOLE -- INITIALIZED ----------------------------------- |\n"); spdlog::info("+-----------------------------------------------------------------------------+\n"); - spdlog::get("console")->set_pattern("[%I:%M:%S:%e] [%L] %v"); + spdlog::get("console")->set_pattern("[%S.%e] %v"); } -void TerminateR5Dev() +void R5Dev_Shutdown() { - RemoveDXHooks(); - Hooks::RemoveHooks(); + Systems_Shutdown(); + +#ifndef DEDICATED + Input_Shutdown(); + DirectX_Shutdown(); +#endif // !DEDICATED + FreeConsole(); } @@ -36,20 +45,19 @@ void TerminateR5Dev() // ENTRYPOINT //############################################################################# -BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) +BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) { - switch (dwReason) { case DLL_PROCESS_ATTACH: { - InitializeR5Dev(); + R5Dev_Init(); break; } case DLL_PROCESS_DETACH: { - TerminateR5Dev(); + R5Dev_Shutdown(); break; } } diff --git a/r5dev/core/init.cpp b/r5dev/core/init.cpp new file mode 100644 index 00000000..7c7bdad1 --- /dev/null +++ b/r5dev/core/init.cpp @@ -0,0 +1,182 @@ +//=============================================================================// +// +// Purpose: Main systems initialization file +// +//=============================================================================// + +#include "core/stdafx.h" +#include "core/init.h" +#include "common/opcodes.h" +#include "tier0/ConCommand.h" +#include "tier0/completion.h" +#include "tier0/cvar.h" +#include "tier0/IConVar.h" +#include "vpc/IAppSystem.h" +#include "vpc/keyvalues.h" +#include "vpc/basefilesystem.h" +#include "vpc/keyvalues.h" +#include "launcher/IApplication.h" +#include "ebisusdk/EbisuSDK.h" +#include "vphysics/QHull.h" +#include "bsplib/bsplib.h" +#ifndef DEDICATED +#include "vgui/CEngineVGui.h" +#include "vgui/vgui_fpspanel.h" +#include "vguimatsurface/MatSystemSurface.h" +#include "client/cdll_engine_int.h" +#endif // !DEDICATED +#include "client/client.h" +#include "client/IVEngineClient.h" +#include "server/server.h" +#include "server/IVEngineServer.h" +#include "squirrel/sqinit.h" +#include "squirrel/sqapi.h" +#include "squirrel/sqvm.h" +#include "rtech/stryder.h" +#include "engine/baseclient.h" +#include "engine/host_state.h" +#include "engine/net_chan.h" +#include "engine/sys_dll.h" +#include "engine/sys_dll2.h" +#include "engine/sys_utils.h" +#ifndef DEDICATED +#include "inputsystem/inputsystem.h" +#include "windows/id3dx.h" +#endif // !DEDICATED + + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// ██╗███╗ ██╗██╗████████╗██╗ █████╗ ██╗ ██╗███████╗ █████╗ ████████╗██╗ ██████╗ ███╗ ██╗ +// ██║████╗ ██║██║╚══██╔══╝██║██╔══██╗██║ ██║╚══███╔╝██╔══██╗╚══██╔══╝██║██╔═══██╗████╗ ██║ +// ██║██╔██╗ ██║██║ ██║ ██║███████║██║ ██║ ███╔╝ ███████║ ██║ ██║██║ ██║██╔██╗ ██║ +// ██║██║╚██╗██║██║ ██║ ██║██╔══██║██║ ██║ ███╔╝ ██╔══██║ ██║ ██║██║ ██║██║╚██╗██║ +// ██║██║ ╚████║██║ ██║ ██║██║ ██║███████╗██║███████╗██║ ██║ ██║ ██║╚██████╔╝██║ ╚████║ +// ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +void Systems_Init() +{ + // Begin the detour transaction to hook the the process + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + // Hook functions + IApplication_Attach(); + CBaseClient_Attach(); + CBaseFileSystem_Attach(); + + //QHull_Attach(); + //BspLib_Attach(); + +#ifndef DEDICATED + CEngineVGui_Attach(); + CFPSPanel_Attach(); + CHLClient_Attach(); +#endif // !DEDICATED + + CServer_Attach(); + +#ifdef DEDICATED + CHostState_Attach(); // Dedicated only for now until backwards compatible with S1. +#endif // DEDICATED + + CNetChan_Attach(); + ConCommand_Attach(); + IConVar_Attach(); + CKeyValueSystem_Attach(); + IVEngineServer_Attach(); + SQAPI_Attach(); + SQVM_Attach(); + SysDll_Attach(); + SysUtils_Attach(); + + // Patch instructions + RuntimePtc_Init(); + + // Commit the transaction + if (DetourTransactionCommit() != NO_ERROR) + { + // Failed to hook into the process, terminate + TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE); + } + + IConVar_InitConVar(); + +#ifdef DEDICATED + Dedicated_Init(); +#endif // DEDICATED + +} + +////////////////////////////////////////////////////////////////////////// +// +// ███████╗██╗ ██╗██╗ ██╗████████╗██████╗ ██████╗ ██╗ ██╗███╗ ██╗ +// ██╔════╝██║ ██║██║ ██║╚══██╔══╝██╔══██╗██╔═══██╗██║ ██║████╗ ██║ +// ███████╗███████║██║ ██║ ██║ ██║ ██║██║ ██║██║ █╗ ██║██╔██╗ ██║ +// ╚════██║██╔══██║██║ ██║ ██║ ██║ ██║██║ ██║██║███╗██║██║╚██╗██║ +// ███████║██║ ██║╚██████╔╝ ██║ ██████╔╝╚██████╔╝╚███╔███╔╝██║ ╚████║ +// ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══╝╚══╝ ╚═╝ ╚═══╝ +// +////////////////////////////////////////////////////////////////////////// + +void Systems_Shutdown() +{ + // Begin the detour transaction to unhook the the process + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + // Unhook functions + IApplication_Detach(); + CBaseClient_Detach(); + CBaseFileSystem_Detach(); + + //QHull_Detach(); + //BspLib_Detach(); + +#ifndef DEDICATED + CEngineVGui_Detach(); + CFPSPanel_Detach(); + CHLClient_Detach(); +#endif // !DEDICATED + CServer_Detach(); + +#ifdef DEDICATED + CHostState_Detach(); // Dedicated only for now until backwards compatible with S1. +#endif // DEDICATED + + CNetChan_Detach(); + ConCommand_Detach(); + IConVar_Detach(); + CKeyValueSystem_Detach(); + IVEngineServer_Detach(); + SQAPI_Detach(); + SQVM_Detach(); + SysDll_Detach(); + SysUtils_Detach(); + + // Commit the transaction + DetourTransactionCommit(); +} + +////////////////////////////////////////////////////////// +// +// ██████╗ ███████╗███████╗██╗ ██╗██╗ ████████╗███████╗ +// ██╔══██╗██╔════╝██╔════╝██║ ██║██║ ╚══██╔══╝██╔════╝ +// ██████╔╝█████╗ ███████╗██║ ██║██║ ██║ ███████╗ +// ██╔══██╗██╔══╝ ╚════██║██║ ██║██║ ██║ ╚════██║ +// ██║ ██║███████╗███████║╚██████╔╝███████╗██║ ███████║ +// ╚═╝ ╚═╝╚══════╝╚══════╝ ╚═════╝ ╚══════╝╚═╝ ╚══════╝ +// +////////////////////////////////////////////////////////// + +void PrintHAddress() // Test the sigscan results +{ + std::cout << "+----------------------------------------------------------------+" << std::endl; + for (IDetour* pdetour : vdetour) + { + pdetour->debugp(); + } +} diff --git a/r5dev/core/init.h b/r5dev/core/init.h new file mode 100644 index 00000000..8ddabd80 --- /dev/null +++ b/r5dev/core/init.h @@ -0,0 +1,10 @@ +#pragma once +namespace +{ + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /* ==== ------- ========================================================================================================================================================= */ +} + +void Systems_Init(); +void Systems_Shutdown(); +void PrintHAddress(); diff --git a/r5dev/core/logdef.h b/r5dev/core/logdef.h new file mode 100644 index 00000000..7a58613e --- /dev/null +++ b/r5dev/core/logdef.h @@ -0,0 +1,23 @@ +#pragma once + +//------------------------------------------------------------------------- +// NETCHAN | +inline auto g_spd_netchan_logger = spdlog::basic_logger_mt("netchan_logger", "platform\\logs\\net_trace.log"); +static std::ostringstream g_spd_net_p_oss; +static auto g_spd_net_p_ostream_sink = std::make_shared(g_spd_net_p_oss); +//------------------------------------------------------------------------- +// FILESYSTEM | +static std::ostringstream fs_oss; +static auto fs_ostream_sink = std::make_shared(fs_oss); +//------------------------------------------------------------------------- +// SQUIRREL PRINTF | +inline std::ostringstream g_spd_sqvm_p_oss; +inline auto g_spd_sqvm_p_ostream_sink = std::make_shared(g_spd_sqvm_p_oss); +//------------------------------------------------------------------------- +// SQUIRREL WARNF | +inline std::ostringstream g_spd_sqvm_w_oss; +inline auto g_spd_sqvm_w_ostream_sink = std::make_shared(g_spd_sqvm_w_oss); +//------------------------------------------------------------------------- +// SYSTEM PRINTF | +inline std::ostringstream g_spd_sys_w_oss; +inline auto g_spd_sys_p_ostream_sink = std::make_shared(g_spd_sys_w_oss); diff --git a/r5dev/include/r5dev.h b/r5dev/core/r5dev.h similarity index 100% rename from r5dev/include/r5dev.h rename to r5dev/core/r5dev.h diff --git a/r5dev/resource.h b/r5dev/core/resource.h similarity index 67% rename from r5dev/resource.h rename to r5dev/core/resource.h index f1b308d1..9efea4f3 100644 --- a/r5dev/resource.h +++ b/r5dev/core/resource.h @@ -1,12 +1,15 @@ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. -// Used by BuildVersion.rc +// Used by r5dev.rc +// +#define PNG 256 +#define IDB_PNG1 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/r5dev/core/stdafx.cpp b/r5dev/core/stdafx.cpp new file mode 100644 index 00000000..1f4c90b9 --- /dev/null +++ b/r5dev/core/stdafx.cpp @@ -0,0 +1 @@ +#include "core/stdafx.h" diff --git a/r5dev/core/stdafx.h b/r5dev/core/stdafx.h new file mode 100644 index 00000000..c910af11 --- /dev/null +++ b/r5dev/core/stdafx.h @@ -0,0 +1,56 @@ +#pragma once +#pragma message("Pre-compiling headers.\n") + +#define WIN32_LEAN_AND_MEAN // Prevent winsock2 redefinition. +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(DEDICATED) +#include +#endif // !DEDICATED + +#include "thirdparty/detours/include/detours.h" +#include "thirdparty/detours/include/idetour.h" + +#if !defined(DEDICATED) +#include "thirdparty/imgui/include/imgui.h" +#include "thirdparty/imgui/include/imgui_utility.h" +#include "thirdparty/imgui/include/imgui_stdlib.h" +#include "thirdparty/imgui/include/imgui_impl_dx11.h" +#include "thirdparty/imgui/include/imgui_impl_win32.h" +#endif // !DEDICATED + +#include "thirdparty/spdlog/include/spdlog.h" +#include "thirdparty/spdlog/include/sinks/basic_file_sink.h" +#include "thirdparty/spdlog/include/sinks/stdout_sinks.h" +#include "thirdparty/spdlog/include/sinks/ostream_sink.h" +#include "public/include/utility.h" +#include "public/include/memaddr.h" +#include "public/include/httplib.h" +#include "public/include/json.hpp" + +#ifndef SDKLAUNCHER +namespace +{ + MODULE g_mGameDll = MODULE("r5apex.exe"); + MODULE g_mRadVideoToolsDll = MODULE("bink2w64.dll"); + MODULE g_mRadAudioDecoderDll = MODULE("binkawin64.dll"); + MODULE g_mRadAudioSystemDll = MODULE("mileswin64.dll"); +} +#endif // SDKLAUNCHER diff --git a/r5dev/cpp.hint b/r5dev/cpp.hint new file mode 100644 index 00000000..15955c79 --- /dev/null +++ b/r5dev/cpp.hint @@ -0,0 +1,9 @@ +// Hint files help the Visual Studio IDE interpret Visual C++ identifiers +// such as names of functions and macros. +// For more information see https://go.microsoft.com/fwlink/?linkid=865984 +#define CIMGUI_API +#define NULL +#define NULL ((void *)0) +#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) +#define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) +#define IM_FMTARGS(FMT) diff --git a/r5dev/dedicated.vcxproj b/r5dev/dedicated.vcxproj new file mode 100644 index 00000000..b31728a8 --- /dev/null +++ b/r5dev/dedicated.vcxproj @@ -0,0 +1,377 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {ed2c50b3-7c2c-4e44-988e-daa059f72b9c} + dedicated + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + MultiByte + Static + + + DynamicLibrary + false + v143 + true + MultiByte + Static + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + $(SolutionDir)r5dev\;$(IncludePath); + $(SolutionDir)r5dev\thirdparty\detours\libs;$(SolutionDir)r5dev\thirdparty\lzham\libs;$(LibraryPath); + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)build\$(ProjectName)\$(Configuration)\ + + + false + $(SolutionDir)r5dev\;$(IncludePath); + $(SolutionDir)r5dev\thirdparty\detours\libs;$(SolutionDir)r5dev\thirdparty\lzham\libs;$(LibraryPath); + $(SolutionDir)bin\$(Configuration)\ + $(SolutionDir)build\$(ProjectName)\$(Configuration)\ + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + /D DEDICATED %(AdditionalOptions) + Use + core\stdafx.h + stdcpp17 + stdc17 + + + Console + true + detours.lib;lzhamlib_x64D.lib;lzhamcomp_x64D.lib;lzhamdecomp_x64D.lib;d3d11.lib;bcrypt.lib;%(AdditionalDependencies) + + + del "..\..\..\$(ProjectName)" && copy /Y "$(TargetPath)" "..\..\..\ + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + /D DEDICATED %(AdditionalOptions) + Use + core\stdafx.h + stdcpp17 + true + Speed + stdc17 + + + Console + true + true + true + detours.lib;lzhamlib_x64.lib;lzhamcomp_x64.lib;lzhamdecomp_x64.lib;d3d11.lib;bcrypt.lib;%(AdditionalDependencies) + + + del "..\..\..\$(ProjectName)" && copy /Y "$(TargetPath)" "..\..\..\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/r5dev/dedicated.vcxproj.filters b/r5dev/dedicated.vcxproj.filters new file mode 100644 index 00000000..a1381cad --- /dev/null +++ b/r5dev/dedicated.vcxproj.filters @@ -0,0 +1,687 @@ + + + + + {f97c7e9b-cc5e-4ec1-8c2a-19cf941b5c90} + + + {c9b133cf-3943-49c9-abb0-c6f9e83a696c} + + + {f280958c-f362-4918-a1f3-ca66f4bf1ec6} + + + {00cd526c-e897-4d88-86e0-03612cfda456} + + + {495a53fc-5055-4449-b42c-b7edc5e83466} + + + {08dd9239-516c-4b9f-8064-28bd127d9806} + + + {a6ab59cc-8df3-4fbd-b94b-2ec6e83feea9} + + + {009673c6-839f-48d7-b2c6-eca9a6e606b1} + + + {08acc045-15dd-469f-b717-35f1dd35c07c} + + + {8786ae3f-91d3-4eb8-89b7-e2135d143faa} + + + {e88d652e-34a0-44a0-a886-8861315c5b4f} + + + {9dc16eaa-182b-42ea-9524-8cc8421829b6} + + + {3792464a-e366-4bbb-86fa-35631e3748f2} + + + {a7be4dd8-3e52-4053-afa5-26faf833e5b7} + + + {3db73603-e800-4763-98dc-f3bf4c674261} + + + {2b79f12d-1506-42e6-94a9-560891c0ff8c} + + + {1a595a55-8bcf-4c0f-8456-4669b8692ce5} + + + {7c06597f-4eda-4695-9d39-d7719ead774e} + + + {a0f19f1c-dfba-49b4-a9d1-2f7c353bf0d8} + + + {6c7f8ba7-fc42-49a3-b22d-c23098e75c0c} + + + {903e9591-e043-4d78-970e-8246198f4d7f} + + + {4f304fe8-8e16-44c3-a173-cb47fb009e23} + + + {2dd28355-d167-479b-8437-855a2186cbb8} + + + {86d45699-fa61-431d-920e-bfdb013b1918} + + + {d4c6d6c0-05da-43ab-a6fc-f61ce721f870} + + + {8aacb674-0b73-41fa-94f0-98ba718289a9} + + + {bbb7a170-37ae-4c76-b99a-8ebb924818ed} + + + {e1897f61-953e-4173-9c75-e59ca33929ea} + + + {e7189077-ca26-4a4e-bb60-eec7e5a1bafc} + + + {24c9de5d-3333-4464-acb2-41f950a879b8} + + + {db372a17-c0de-4a8b-8aa8-9b3129cc5955} + + + {8288ba1a-7609-42ef-af3b-850727635a99} + + + + + sdk\client + + + sdk\client + + + sdk\common + + + sdk\common + + + core + + + core + + + core + + + core + + + sdk\ebisusdk + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\launcher + + + sdk\mathlib + + + sdk\networksystem + + + sdk\networksystem + + + sdk\public\include + + + sdk\public\include + + + sdk\public\include + + + sdk\public\include + + + sdk\public\include + + + sdk\rtech + + + sdk\rtech + + + sdk\server + + + sdk\squirrel + + + sdk\squirrel + + + thirdparty\detours\include + + + thirdparty\detours\include + + + thirdparty\detours\include + + + thirdparty\detours\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\sinks + + + thirdparty\spdlog\include\fmt + + + thirdparty\spdlog\include\fmt + + + thirdparty\spdlog\include\fmt + + + thirdparty\spdlog\include\fmt + + + thirdparty\spdlog\include\fmt + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\fmt\bundled + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\cfg + + + thirdparty\spdlog\include\cfg + + + thirdparty\spdlog\include\cfg + + + thirdparty\spdlog\include\cfg + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\vpc + + + sdk\vpc + + + sdk\vpc + + + sdk\vpc + + + sdk\vphysics + + + windows + + + sdk\networksystem + + + sdk\mathlib + + + sdk\mathlib + + + sdk\squirrel + + + sdk\server + + + sdk\tier0 + + + sdk\mathlib + + + sdk\vpklib + + + sdk\public\include + + + thirdparty\lzham\include + + + thirdparty\lzham\include + + + sdk\mathlib + + + sdk\bsplib + + + sdk\common + + + + + sdk\client + + + sdk\client + + + sdk\common + + + core + + + core + + + core + + + sdk\ebisusdk + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\launcher + + + sdk\public + + + sdk\public + + + sdk\rtech + + + sdk\rtech + + + sdk\server + + + sdk\squirrel + + + sdk\squirrel + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\vpc + + + sdk\vpc + + + sdk\vpc + + + sdk\vphysics + + + windows + + + sdk\networksystem + + + sdk\mathlib + + + sdk\squirrel + + + sdk\server + + + sdk\vpklib + + + sdk\public + + + sdk\bsplib + + + sdk\mathlib + + + sdk\mathlib + + + + + + \ No newline at end of file diff --git a/r5dev/ebisusdk/EbisuSDK.cpp b/r5dev/ebisusdk/EbisuSDK.cpp new file mode 100644 index 00000000..4e865c3a --- /dev/null +++ b/r5dev/ebisusdk/EbisuSDK.cpp @@ -0,0 +1,26 @@ +#include "core/stdafx.h" +#include "ebisusdk/EbisuSDK.h" +#include "client/client.h" +#include "engine/sys_utils.h" + +//----------------------------------------------------------------------------- +// Purpose: sets the EbisuSDK globals for dedicated to satisfy command callbacks +//----------------------------------------------------------------------------- +void HEbisuSDK_Init() +{ +#ifdef DEDICATED + *(char*)g_bEbisuSDKInitialized.GetPtr() = (char)0x1; // <- 1st EbisuSDK + *(char*)g_bEbisuSDKCvarInitialized.GetPtr() = (char)0x1; // <- 2nd EbisuSDK + *(char*)g_qEbisuSDKCvarInitialized.GetPtr() = (char)0x1; // <- 3rd EbisuSDK +#endif // DEDICATED +} + +void EbisuSDK_Attach() +{ + // +} + +void EbisuSDK_Detach() +{ + // +} diff --git a/r5dev/ebisusdk/EbisuSDK.h b/r5dev/ebisusdk/EbisuSDK.h new file mode 100644 index 00000000..41157b8e --- /dev/null +++ b/r5dev/ebisusdk/EbisuSDK.h @@ -0,0 +1,64 @@ +#pragma once +#include "tier0/basetypes.h" +#include "tier0/completion.h" +#include "public/include/utility.h" + +namespace +{ +#ifdef DEDICATED + ADDRESS p_EbisuSDK_Init_Tier0 = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x80\x3D\x00\x00\x00\x00\x00\x0F\x85\x00\x02\x00\x00\x48\x89\x5C\x24\x20", "xxxxxx????xxx?xxxxxxxx").GetPtr(); + void(*EbisuSDK_Init_Tier0) = (void(*))p_EbisuSDK_Init_Tier0.GetPtr(); /*48 83 EC 28 80 3D ?? ?? ?? ?? 00 0F 85 ?? 02 00 00 48 89 5C 24 20*/ + + ADDRESS p_EbisuSDK_CVar_Init = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x57\x48\x83\xEC\x40\x83\x3D", "xxxxxxxx"); + void(*EbisuSDK_CVar_Init) = (void(*))p_EbisuSDK_CVar_Init.GetPtr(); /*40 57 48 83 EC 40 83 3D*/ +#else + ADDRESS p_OriginGetErrorDescription = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x8B\xD9\x48\x8D\x15\x00\x00\x00\x00", "xxxxxxxxxxx????").GetPtr(); + void(*OriginGetErrorDescription) = (void(*))p_OriginGetErrorDescription.GetPtr(); /*40 53 48 83 EC 20 8B D9 48 8D 15 ?? ?? ?? ??*/ +#endif +} + +namespace +{ +#ifdef DEDICATED +#if defined (GAMEDLL_S1) + ADDRESS g_bEbisuSDKInitialized = p_EbisuSDK_Init_Tier0.FindPatternSelf("80 3D ?? ?? ?? ?? 00", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).GetPtr(); + ADDRESS g_bEbisuSDKCvarInitialized = p_Map_Callback.FindPatternSelf("80 3D 8F 7C 1E 22 00", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).GetPtr(); + ADDRESS g_qEbisuSDKCvarInitialized = p_EbisuSDK_CVar_Init.FindPatternSelf("4C 89 05 C4 2B 0E 22", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(); +#elif defined (GAMEDLL_S2) + ADDRESS g_bEbisuSDKInitialized = p_EbisuSDK_Init_Tier0.FindPatternSelf("80 3D ?? ?? ?? ?? 00", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).GetPtr(); + ADDRESS g_bEbisuSDKCvarInitialized = p_Map_Callback.FindPatternSelf("80 3D 43 2D 41 22 00", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).GetPtr(); + ADDRESS g_qEbisuSDKCvarInitialized = p_EbisuSDK_CVar_Init.FindPatternSelf("4C 89 05 74 2D 32 22", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(); +#elif defined (GAMEDLL_S3) + ADDRESS g_bEbisuSDKInitialized = p_EbisuSDK_Init_Tier0.FindPatternSelf("80 3D ?? ?? ?? ?? 00", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).GetPtr(); + ADDRESS g_bEbisuSDKCvarInitialized = p_Map_Callback.FindPatternSelf("80 3D 23 54 2B 23 00", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).GetPtr(); + ADDRESS g_qEbisuSDKCvarInitialized = p_EbisuSDK_CVar_Init.FindPatternSelf("4C 89 05 B4 2C 1C 23", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(); +#endif // GAMEDLL_* +#endif // DEDICATED +} +/////////////////////////////////////////////////////////////////////////////// +void HEbisuSDK_Init(); + +void EbisuSDK_Attach(); +void EbisuSDK_Detach(); + + +/////////////////////////////////////////////////////////////////////////////// +class HEbisuSDK : public IDetour +{ + virtual void debugp() + { +#ifdef DEDICATED + std::cout << "| FUN: EbisuSDK_Init_Tier0 : 0x" << std::hex << std::uppercase << p_EbisuSDK_Init_Tier0.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: EbisuSDK_CVar_Init : 0x" << std::hex << std::uppercase << p_EbisuSDK_CVar_Init.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_bEbisuSDKInitialized : 0x" << std::hex << std::uppercase << g_bEbisuSDKInitialized.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_bEbisuSDKCvarInitialized : 0x" << std::hex << std::uppercase << g_bEbisuSDKCvarInitialized.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_qEbisuSDKCvarInitialized : 0x" << std::hex << std::uppercase << g_qEbisuSDKCvarInitialized.GetPtr() << std::setw(npad) << " |" << std::endl; +#else + std::cout << "| FUN: OriginGetErrorDescription : 0x" << std::hex << std::uppercase << p_OriginGetErrorDescription.GetPtr() << std::setw(npad) << " |" << std::endl; +#endif // DEDICATED + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HEbisuSDK); diff --git a/r5dev/ebisusdk/EbisuTypes.h b/r5dev/ebisusdk/EbisuTypes.h new file mode 100644 index 00000000..aea65b49 --- /dev/null +++ b/r5dev/ebisusdk/EbisuTypes.h @@ -0,0 +1,137 @@ +#pragma once + +//----------------------------------------------------------------------------- +// General errors +//----------------------------------------------------------------------------- +#define ORIGIN_ERROR 0xA0000000 +#define ORIGIN_WARNING 0x40000000 +#define ORIGIN_ERROR_AREA_GENERAL 0x00000000 +#define ORIGIN_ERROR_AREA_SDK (1<<16) +#define ORIGIN_ERROR_AREA_CORE (2<<16) +#define ORIGIN_ERROR_AREA_IGO (3<<16) +#define ORIGIN_ERROR_AREA_FRIENDS (4<<16) +#define ORIGIN_ERROR_AREA_PRESENCE (5<<16) +#define ORIGIN_ERROR_AREA_COMMERCE (6<<16) +#define ORIGIN_ERROR_AREA_ACHIEVEMENTS (7<<16) +#define ORIGIN_ERROR_AREA_LSX (8<<16) +#define ORIGIN_ERROR_AREA_PROXY (9<<16) +#define ORIGIN_ERROR_LEVEL_SHIFT 24 +#define ORIGIN_ERROR_LEVEL_MASK 0x0F000000 +#define ORIGIN_LEVEL_0 (0<<24) +#define ORIGIN_LEVEL_1 (1<<24) +#define ORIGIN_LEVEL_2 (2<<24) +#define ORIGIN_LEVEL_3 (3<<24) +#define ORIGIN_LEVEL_4 (4<<24) + +//----------------------------------------------------------------------------- +// Origin errors +//----------------------------------------------------------------------------- +#define ORIGIN_SUCCESS 0 +#define ORIGIN_PENDING 1 +#define ORIGIN_ERROR_GENERAL -1 +#define ORIGIN_ERROR_INVALID_HANDLE (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_GENERAL + 0) +#define ORIGIN_ERROR_OUT_OF_MEMORY (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_GENERAL + 1) +#define ORIGIN_ERROR_NOT_IMPLEMENTED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_GENERAL + 2) +#define ORIGIN_ERROR_INVALID_USER (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 3) +#define ORIGIN_ERROR_INVALID_ARGUMENT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 4) +#define ORIGIN_ERROR_NO_CALLBACK_SPECIFIED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 5) +#define ORIGIN_ERROR_BUFFER_TOO_SMALL (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 6) +#define ORIGIN_ERROR_TOO_MANY_VALUES_IN_LIST (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 7) +#define ORIGIN_ERROR_NOT_FOUND (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 8) +#define ORIGIN_ERROR_INVALID_PERSONA (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 9) +#define ORIGIN_ERROR_NO_NETWORK (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 10) +#define ORIGIN_ERROR_NO_SERVICE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 11) +#define ORIGIN_ERROR_NOT_LOGGED_IN (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 12) +#define ORIGIN_ERROR_MANDATORY_ORIGIN_UPDATE_PENDING (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 13) +#define ORIGIN_ERROR_ACCOUNT_IN_USE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 14) +#define ORIGIN_ERROR_TOO_MANY_INSTANCES (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_GENERAL + 15) +#define ORIGIN_ERROR_ALREADY_EXISTS (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_GENERAL + 16) +#define ORIGIN_ERROR_INVALID_OPERATION (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 17) +#define ORIGIN_ERROR_AGE_RESTRICTED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 18) +#define ORIGIN_ERROR_BANNED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 19) +#define ORIGIN_ERROR_NOT_READY (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 20) + +//----------------------------------------------------------------------------- +// SDK errors +//----------------------------------------------------------------------------- +#define ORIGIN_ERROR_SDK_NOT_INITIALIZED (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_SDK + 0) +#define ORIGIN_ERROR_SDK_INVALID_ALLOCATOR_DEALLOCATOR_COMBINATION (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_SDK + 1) +#define ORIGIN_ERROR_SDK_IS_RUNNING (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 2) +#define ORIGIN_ERROR_SDK_NOT_ALL_RESOURCES_RELEASED (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_SDK + 3) +#define ORIGIN_ERROR_SDK_INVALID_RESOURCE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 4) +#define ORIGIN_ERROR_SDK_INTERNAL_ERROR (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_SDK + 5) +#define ORIGIN_ERROR_SDK_INTERNAL_BUFFER_TOO_SMALL (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_SDK + 6) + +//----------------------------------------------------------------------------- +// SDK warnings +//----------------------------------------------------------------------------- +#define ORIGIN_WARNING_SDK_ALREADY_INITIALIZED (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 1) +#define ORIGIN_WARNING_SDK_STILL_RUNNING (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 2) +#define ORIGIN_WARNING_SDK_ENUMERATOR_IN_USE (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 3) +#define ORIGIN_WARNING_SDK_ENUMERATOR_TERMINATED (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 4) + +//----------------------------------------------------------------------------- +// Core errors +//----------------------------------------------------------------------------- +#define ORIGIN_ERROR_CORE_NOTLOADED (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_CORE + 0) +#define ORIGIN_ERROR_CORE_LOGIN_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 1) +#define ORIGIN_ERROR_CORE_AUTHENTICATION_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 2 +#define ORIGIN_ERROR_CORE_SEND_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 4) +#define ORIGIN_ERROR_CORE_RECEIVE_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 5) +#define ORIGIN_ERROR_CORE_RESOURCE_NOT_FOUND (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 6) +#define ORIGIN_ERROR_CORE_INCOMPATIBLE_VERSION (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_CORE + 7) +#define ORIGIN_ERROR_CORE_NOT_INSTALLED (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_CORE + 8) + +//----------------------------------------------------------------------------- +// In-game overlay errors +//----------------------------------------------------------------------------- +#define ORIGIN_WARNING_IGO_NOTLOADED (ORIGIN_WARNING + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_IGO + 0) +#define ORIGIN_WARNING_IGO_SUPPORT_NOTLOADED (ORIGIN_WARNING + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_IGO + 1) +#define ORIGIN_ERROR_IGO_ILLEGAL_ANCHOR_POINT (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_IGO + 2) +#define ORIGIN_ERROR_IGO_ILLEGAL_DOCK_POINT (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_IGO + 3) +#define ORIGIN_ERROR_IGO_NOT_AVAILABLE (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_IGO + 4) + +//----------------------------------------------------------------------------- +// Presence errors +//----------------------------------------------------------------------------- +#define ORIGIN_ERROR_NO_MULTIPLAYER_ID (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PRESENCE + 0) + +//----------------------------------------------------------------------------- +// LSX errors +//----------------------------------------------------------------------------- +#define ORIGIN_ERROR_LSX_INVALID_RESPONSE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_LSX + 0) +#define ORIGIN_ERROR_LSX_NO_RESPONSE (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_LSX + 1) +#define ORIGIN_ERROR_LSX_INVALID_REQUEST (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_LSX + 2) + +//----------------------------------------------------------------------------- +// Commerce errors +//----------------------------------------------------------------------------- +#define ORIGIN_ERROR_COMMERCE_NO_SUCH_STORE (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_COMMERCE + 0) +#define ORIGIN_ERROR_COMMERCE_NO_SUCH_CATALOG (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_COMMERCE + 1) +#define ORIGIN_ERROR_COMMERCE_INVALID_REPLY (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_COMMERCE + 2) +#define ORIGIN_ERROR_COMMERCE_NO_CATEGORIES (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 3) +#define ORIGIN_ERROR_COMMERCE_NO_PRODUCTS (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 4) +#define ORIGIN_ERROR_COMMERCE_UNDERAGE_USER (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 5) +#define ORIGIN_ERROR_COMMERCE_DEPRECATED_STORE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 6) +#define ORIGIN_ERROR_PROXY (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 0) +#define ORIGIN_SUCCESS_PROXY_OK (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 200) +#define ORIGIN_SUCCESS_PROXY_CREATED (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 201) +#define ORIGIN_SUCCESS_PROXY_ACCEPTED (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 202) +#define ORIGIN_SUCCESS_PROXY_NON_AUTH_INFO (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 203) +#define ORIGIN_SUCCESS_PROXY_NO_CONTENT (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 204) +#define ORIGIN_SUCCESS_RESET_CONTENT (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 205) +#define ORIGIN_SUCCESS_PARTIAL_CONTENT (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 206) +#define ORIGIN_ERROR_PROXY_BAD_REQUEST (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 400) +#define ORIGIN_ERROR_PROXY_UNAUTHORIZED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 401) +#define ORIGIN_ERROR_PROXY_PAYMENT_REQUIRED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 402) +#define ORIGIN_ERROR_PROXY_FORBIDDEN (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 403) +#define ORIGIN_ERROR_PROXY_NOT_FOUND (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 404) +#define ORIGIN_ERROR_PROXY_METHOD_NOT_ALLOWED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 405) +#define ORIGIN_ERROR_PROXY_NOT_ACCEPTABLE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 406) +#define ORIGIN_ERROR_PROXY_REQUEST_TIMEOUT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 408) +#define ORIGIN_ERROR_PROXY_CONFLICT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 409) +#define ORIGIN_ERROR_PROXY_INTERNAL_ERROR (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 500) +#define ORIGIN_ERROR_PROXY_NOT_IMPLEMENTED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 501) +#define ORIGIN_ERROR_PROXY_BAD_GATEWAY (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 502) +#define ORIGIN_ERROR_PROXY_SERVICE_UNAVAILABLE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 503) +#define ORIGIN_ERROR_PROXY_GATEWAY_TIMEOUT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 504) diff --git a/r5dev/engine/baseclient.cpp b/r5dev/engine/baseclient.cpp new file mode 100644 index 00000000..2fe8abbb --- /dev/null +++ b/r5dev/engine/baseclient.cpp @@ -0,0 +1,20 @@ +#include "core/stdafx.h" +#include "engine/baseclient.h" + +//--------------------------------------------------------------------------------- +// Purpose: throw away any residual garbage in the channel +//--------------------------------------------------------------------------------- +std::int64_t* HCBaseClient_Clear(std::int64_t client) +{ + return CBaseClient_Clear(client); +} + +/////////////////////////////////////////////////////////////////////////////// +void CBaseClient_Attach() +{ + DetourAttach((LPVOID*)&CBaseClient_Clear, &HCBaseClient_Clear); +} +void CBaseClient_Detach() +{ + DetourDetach((LPVOID*)&CBaseClient_Clear, &HCBaseClient_Clear); +} diff --git a/r5dev/engine/baseclient.h b/r5dev/engine/baseclient.h new file mode 100644 index 00000000..cb929954 --- /dev/null +++ b/r5dev/engine/baseclient.h @@ -0,0 +1,27 @@ +#pragma once + +namespace +{ + /* ==== CBASECLIENT ===================================================================================================================================================== */ + ADDRESS p_CBaseClient_Clear = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x41\x56\x41\x57\x48\x83\xEC\x20\x48\x8B\xD9\x48\x89\x74", "xxxxxxxxxxxxxxxx"); + std::int64_t* (*CBaseClient_Clear)(std::int64_t client) = (std::int64_t * (*)(std::int64_t))p_CBaseClient_Clear.GetPtr(); /*40 53 41 56 41 57 48 83 EC 20 48 8B D9 48 89 74*/ +} + +/////////////////////////////////////////////////////////////////////////////// +std::int64_t* HCBaseClient_Clear(std::int64_t client); + +void CBaseClient_Attach(); +void CBaseClient_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HBaseClient : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CBaseClient::Clear : 0x" << std::hex << std::uppercase << p_CBaseClient_Clear.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HBaseClient); diff --git a/r5dev/engine/baseclientstate.cpp b/r5dev/engine/baseclientstate.cpp new file mode 100644 index 00000000..9d9576b4 --- /dev/null +++ b/r5dev/engine/baseclientstate.cpp @@ -0,0 +1,9 @@ +//===========================================================================// +// +// Purpose: +// +//===========================================================================// + +#include "core/stdafx.h" +#include "engine/baseclientstate.h" +//TODO diff --git a/r5dev/engine/baseclientstate.h b/r5dev/engine/baseclientstate.h new file mode 100644 index 00000000..3f44032f --- /dev/null +++ b/r5dev/engine/baseclientstate.h @@ -0,0 +1,31 @@ +#pragma once +#include "tier0/basetypes.h" + +namespace +{ + /* ==== CCLIENTSTATE ==================================================================================================================================================== */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + //ADDRESS p_CClientState__CheckForResend = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x56\x57\x41\x57\x00\x81\xEC\x20\x04\x00\x00\x45\x0F\xB6\xF9\x00\x00\x00\x00\x8B\xF1\x48", "xxxx?xxxx?xxxx?xxxxx????xxx"); /*48 89 5C 24 ?? 56 57 41 57 ?? 81 EC 20 04 ?? 00 45 0F B6 F9 ?? ?? ?? ?? 8B F1 48*/ + //void (*CClientState__CheckForResend)(std::int64_t a1, const char* a2, std::int64_t a3, char a4, int a5, std::uint8_t* a6) = (void(*)(std::int64_t, const char*, std::int64_t, char, int, std::uint8_t*))p_CClientState__CheckForResend.GetPtr(); +#elif defined (GAMEDLL_S2) + //ADDRESS p_CClientState__CheckForResend = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x74\x24\x00\x48\x89\x7C\x24\x00\x41\x56\x48\x81\xEC\x00\x00\x00\x00\x45\x0F\xB6", "xxxx?xxxx?xxxx?xxxxx????xxx"); /*48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 81 EC ?? ?? ?? ?? 45 0F B6*/ + //void (*CClientState__CheckForResend)(std::int64_t a1, const char* a2, std::int64_t a3, char a4, int a5, std::uint8_t* a6) = (void(*)(std::int64_t, const char*, std::int64_t, char, int, std::uint8_t*))p_CClientState__CheckForResend.GetPtr(); +#elif defined (GAMEDLL_S3) + //ADDRESS p_CClientState__CheckForResend = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x74\x24\x00\x48\x89\x7C\x24\x00\x41\x56\x48\x81\xEC\x00\x00\x00\x00\x48\x8B\x32", "xxxx?xxxx?xxxx?xxxxx????xxx"); /*48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 81 EC ?? ?? ?? ?? 48 8B 32*/ + //void (*CClientState__CheckForResend)(std::int64_t a1, const char* a2, std::int64_t a3, char a4, int a5, std::uint8_t* a6) = (void(*)(std::int64_t, const char*, std::int64_t, char, int, std::uint8_t*))p_CClientState__CheckForResend.GetPtr(); +#endif +} + + +/////////////////////////////////////////////////////////////////////////////// +class HClientState : public IDetour +{ + virtual void debugp() + { + //std::cout << "| FUN: CClientState::CheckForResend : 0x" << std::hex << std::uppercase << p_CClientState__CheckForResend.GetPtr() << std::setw(npad) << " |" << std::endl; + //std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HClientState); diff --git a/r5dev/engine/host_state.cpp b/r5dev/engine/host_state.cpp new file mode 100644 index 00000000..b8cdcde9 --- /dev/null +++ b/r5dev/engine/host_state.cpp @@ -0,0 +1,244 @@ +#include "core/stdafx.h" +#include "engine/sys_utils.h" +#include "engine/host_state.h" +#include "engine/net_chan.h" +#include "tier0/cvar.h" +#include "client/IVEngineClient.h" + +//----------------------------------------------------------------------------- +// Purpose: state machine's main processing loop +//----------------------------------------------------------------------------- +void HCHostState_FrameUpdate(void* rcx, void* rdx, float time) +{ + static auto setjmpFn = ADDRESS(0x141205460).RCast(); + static auto host_abortserver = ADDRESS(0x14B37C700).RCast(); + static auto CHostState_InitFn = ADDRESS(0x14023E7D0).RCast(); + static auto g_ServerAbortServer = ADDRESS(0x14B37CA22).RCast(); + static auto State_RunFn = ADDRESS(0x14023E870).RCast(); + static auto Cbuf_ExecuteFn = ADDRESS(0x14020D5C0).RCast(); + static auto g_ServerGameClients = ADDRESS(0x14B383428).RCast(); + static auto SV_InitGameDLLFn = ADDRESS(0x140308B90).RCast(); + static auto g_CModelLoader = ADDRESS(0x14173B210).RCast(); + static auto CModelLoader_Map_IsValidFn = ADDRESS(0x1402562F0).RCast(); + static auto Host_NewGameFn = ADDRESS(0x140238DA0).RCast(); + static auto Host_Game_ShutdownFn = ADDRESS(0x14023EDA0).RCast(); + static auto src_drawloading = ADDRESS(0x14B37D96B).RCast(); + static auto scr_engineevent_loadingstarted = ADDRESS(0x1666ED024).RCast(); + static auto gfExtendedError = ADDRESS(0x14B383391).RCast(); + static auto g_CEngineVGui = ADDRESS(0x141741310).RCast(); + static auto g_ServerDLL = ADDRESS(0x141732048).RCast(); + static auto Host_ChangelevelFn = ADDRESS(0x1402387B0).RCast(); + static auto CL_EndMovieFn = ADDRESS(0x1402C03D0).RCast(); + static auto SendOfflineRequestToStryderFn = ADDRESS(0x14033D380).RCast(); + static auto CEngine = ADDRESS(0X141741BA0).RCast(); + + static bool bInitialized = false; + if (!bInitialized) + { + IConVar_ClearHostNames(); + ConCommand_InitConCommand(); + + IVEngineClient_CommandExecute(NULL, "exec autoexec.cfg"); + IVEngineClient_CommandExecute(NULL, "exec autoexec_server.cfg"); +#ifndef DEDICATED + IVEngineClient_CommandExecute(NULL, "exec autoexec_client.cfg"); +#endif // !DEDICATED + + *(bool*)m_bRestrictServerCommands = true; // Restrict commands. + void* disconnect = g_pCvar->FindCommand("disconnect"); + *(std::int32_t*)((std::uintptr_t)disconnect + 0x38) |= FCVAR_SERVER_CAN_EXECUTE; // Make sure server is not restricted to this. + + if (net_userandomkey->m_pParent->m_iValue == 1) + { + HNET_GenerateKey(); + } + + g_pCvar->FindVar("net_usesocketsforloopback")->m_pParent->m_iValue = 1; + + bInitialized = true; + } + + HostStates_t oldState{}; + void* placeHolder = nullptr; + if (setjmpFn(*host_abortserver, placeHolder)) + { + CHostState_InitFn(g_pHostState); + return; + } + else + { + *g_ServerAbortServer = true; + + do + { + Cbuf_ExecuteFn(); + oldState = g_pHostState->m_iCurrentState; + switch (g_pHostState->m_iCurrentState) + { + case HostStates_t::HS_NEW_GAME: + { + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_NEW_GAME | Loading level: '%s'\n", g_pHostState->m_levelName); + + // Inlined CHostState::State_NewGame + g_pHostState->m_bSplitScreenConnect = false; + if (!g_ServerGameClients) // Init Game if it ain't valid. + { + SV_InitGameDLLFn(); + } + + if (!CModelLoader_Map_IsValidFn(g_CModelLoader, g_pHostState->m_levelName) // Check if map is valid and if we can start a new game. + || !Host_NewGameFn(g_pHostState->m_levelName, nullptr, g_pHostState->m_bBackgroundLevel, g_pHostState->m_bSplitScreenConnect, nullptr) || !g_ServerGameClients) + { + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_NEW_GAME | Error: Map not valid.\n"); + // Inlined SCR_EndLoadingPlaque + if (*src_drawloading) + { + *scr_engineevent_loadingstarted = 0; + using HideLoadingPlaqueFn = void(*)(void*); + (*reinterpret_cast(g_CEngineVGui))[36](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGui + 36))(&g_CEngineVGui);// HideLoadingPlaque + } + else if (*gfExtendedError) + { + using ShowErrorMessageFn = void(*)(void*); + (*reinterpret_cast(g_CEngineVGui))[35](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGui + 35))(&g_CEngineVGui);// ShowErrorMessage + } + // End Inline SCR_EndLoadingPlaque + + // Inlined CHostState::GameShutdown + if (g_pHostState->m_bActiveGame) + { + using GameShutdownFn = void(*)(void*); + (*reinterpret_cast(g_ServerDLL))[9](g_ServerDLL); // (*(void(__fastcall**)(void*))(*(_QWORD*)g_ServerDLL + 72i64))(g_ServerDLL);// GameShutdown + g_pHostState->m_bActiveGame = 0; + } + // End Inline CHostState::GameShutdown + } + + // Seems useless so nope. + // if (g_CHLClient) + // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); + + g_pHostState->m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. + + // If our next state isn't a shutdown or its a forced shutdown then set next state to run. + if (g_pHostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !g_pCvar->FindVar("host_hasIrreversibleShutdown")->m_pParent->m_iValue) + { + g_pHostState->m_iNextState = HostStates_t::HS_RUN; + } + + // End Inline CHostState::State_NewGame + break; + } + case HostStates_t::HS_CHANGE_LEVEL_SP: + { + g_pHostState->m_flShortFrameTime = 1.5; // Set frame time. + + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_CHANGE_LEVEL_SP | Changing singleplayer level to: '%s'\n", g_pHostState->m_levelName); + + if (CModelLoader_Map_IsValidFn(g_CModelLoader, g_pHostState->m_levelName)) // Check if map is valid and if we can start a new game. + { + Host_ChangelevelFn(true, g_pHostState->m_levelName, g_pHostState->m_mapGroupName); // Call change level as singleplayer level. + } + else + { + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_CHANGE_LEVEL_SP | Error: Unable to find map: '%s'\n", g_pHostState->m_levelName); + } + + // Seems useless so nope. + // if (g_CHLClient) + // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); + + g_pHostState->m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. + + // If our next state isn't a shutdown or its a forced shutdown then set next state to run. + if (g_pHostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !g_pCvar->FindVar("host_hasIrreversibleShutdown")->m_pParent->m_iValue) + { + g_pHostState->m_iNextState = HostStates_t::HS_RUN; + } + + break; + } + case HostStates_t::HS_CHANGE_LEVEL_MP: + { + g_pHostState->m_flShortFrameTime = 0.5; // Set frame time. + using LevelShutdownFn = void(__thiscall*)(void*); + (*reinterpret_cast(*g_ServerDLL))[8](g_ServerDLL); // (*(void (__fastcall **)(void *))(*(_QWORD *)server_dll_var + 64i64))(server_dll_var);// LevelShutdown + + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_CHANGE_LEVEL_MP | Changing multiplayer level to: '%s'\n", g_pHostState->m_levelName); + + if (CModelLoader_Map_IsValidFn(g_CModelLoader, g_pHostState->m_levelName)) // Check if map is valid and if we can start a new game. + { + using EnabledProgressBarForNextLoadFn = void(*)(void*); + (*reinterpret_cast(g_CEngineVGui))[31](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGUI + 31))(&g_CEngineVGUI);// EnabledProgressBarForNextLoad + Host_ChangelevelFn(false, g_pHostState->m_levelName, g_pHostState->m_mapGroupName); // Call change level as multiplayer level. + } + else + { + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_CHANGE_LEVEL_MP | Error: Unable to find map: '%s'\n", g_pHostState->m_levelName); + } + + // Seems useless so nope. + // // if (g_CHLClient) + // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); + + g_pHostState->m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. + + // If our next state isn't a shutdown or its a forced shutdown then set next state to run. + if (g_pHostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !g_pCvar->FindVar("host_hasIrreversibleShutdown")->m_pParent->m_iValue) + { + g_pHostState->m_iNextState = HostStates_t::HS_RUN; + } + + break; + } + case HostStates_t::HS_RUN: + { + State_RunFn(&g_pHostState->m_iCurrentState, nullptr, time); + break; + } + case HostStates_t::HS_GAME_SHUTDOWN: + { + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_GAME_SHUTDOWN | Shutdown game\n"); + Host_Game_ShutdownFn(g_pHostState); + break; + } + case HostStates_t::HS_RESTART: + { + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_RESTART | Restarting client\n"); + CL_EndMovieFn(); + SendOfflineRequestToStryderFn(); // We have hostnames nulled anyway. + *(std::int32_t*)((std::uintptr_t)CEngine + 0xC) = 3; //g_CEngine.vtable->SetNextState(&g_CEngine, DLL_RESTART); + break; + } + case HostStates_t::HS_SHUTDOWN: + { + DevMsg(eDLL_T::ENGINE, "CHostState::FrameUpdate | CASE:HS_SHUTDOWN | Shutdown client\n"); + CL_EndMovieFn(); + SendOfflineRequestToStryderFn(); // We have hostnames nulled anyway. + *(std::int32_t*)((std::uintptr_t)CEngine + 0xC) = 2; //g_CEngine.vtable->SetNextState(&g_CEngine, DLL_CLOSE); + break; + } + default: + { + break; + } + } + + } while ((oldState != HostStates_t::HS_RUN || g_pHostState->m_iNextState == HostStates_t::HS_LOAD_GAME && g_pCvar->FindVar("g_single_frame_shutdown_for_reload_cvar")->m_pParent->m_iValue) + && oldState != HostStates_t::HS_SHUTDOWN + && oldState != HostStates_t::HS_RESTART); + + } +} + +void CHostState_Attach() +{ + DetourAttach((LPVOID*)&CHostState_FrameUpdate, &HCHostState_FrameUpdate); +} + +void CHostState_Detach() +{ + DetourDetach((LPVOID*)&CHostState_FrameUpdate, &HCHostState_FrameUpdate); +} + +CHostState* g_pHostState = reinterpret_cast(p_CHostState_FrameUpdate.FindPatternSelf("48 8D ?? ?? ?? ?? 01", ADDRESS::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr());; diff --git a/r5dev/engine/host_state.h b/r5dev/engine/host_state.h new file mode 100644 index 00000000..a16cb06f --- /dev/null +++ b/r5dev/engine/host_state.h @@ -0,0 +1,65 @@ +#pragma once +#include "mathlib/vector.h" + +enum class HostStates_t : int +{ + HS_NEW_GAME = 0x0, + HS_LOAD_GAME = 0x1, + HS_CHANGE_LEVEL_SP = 0x2, + HS_CHANGE_LEVEL_MP = 0x3, + HS_RUN = 0x4, + HS_GAME_SHUTDOWN = 0x5, + HS_SHUTDOWN = 0x6, + HS_RESTART = 0x7, +}; + +class CHostState +{ +public: + HostStates_t m_iCurrentState; //0x0000 + HostStates_t m_iNextState; //0x0004 + Vector3 m_vecLocation; //0x0008 + QAngle m_angLocation; //0x0014 + char m_levelName[64]; //0x0020 + char m_mapGroupName[256]; //0x0060 + char m_landMarkName[256]; //0x0160 + float m_flShortFrameTime; //0x0260 + bool m_bActiveGame; //0x0264 + bool m_bRememberLocation; //0x0265 + bool m_bBackgroundLevel; //0x0266 + bool m_bWaitingForConnection; //0x0267 + bool m_bSplitScreenConnect; //0x0268 + bool m_bGameHasShutDownAndFlushedMemory; //0x0269 + bool m_bWorkshopMapDownloadPending; //0x026A +}; + +namespace +{ + /* ==== CHOSTSTATE ====================================================================================================================================================== */ + ADDRESS p_CHostState_FrameUpdate = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x20\xF3\x0F\x11\x54\x24\x18", "xxxxxxxxxxxxxxxx"); + void (*CHostState_FrameUpdate)(void* rcx, void* rdx, float time) = (void(*)(void*, void*, float))p_CHostState_FrameUpdate.GetPtr(); /*48 89 5C 24 08 48 89 6C 24 20 F3 0F 11 54 24 18*/ +} + +/////////////////////////////////////////////////////////////////////////////// +void HCHostState_FrameUpdate(void* rcx, void* rdx, float time); + +void CHostState_Attach(); +void CHostState_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +extern CHostState* g_pHostState; + + +/////////////////////////////////////////////////////////////////////////////// +class HHostState : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CHostState::FrameUpdate : 0x" << std::hex << std::uppercase << p_CHostState_FrameUpdate.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pHostState : 0x" << std::hex << std::uppercase << g_pHostState << std::setw(0) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HHostState); diff --git a/r5dev/engine/net_chan.cpp b/r5dev/engine/net_chan.cpp new file mode 100644 index 00000000..659c48e0 --- /dev/null +++ b/r5dev/engine/net_chan.cpp @@ -0,0 +1,176 @@ +//=============================================================================// +// +// Purpose: Netchannel system utilities +// +//=============================================================================// + +#include "core/stdafx.h" +#include "core/logdef.h" +#include "tier0/cvar.h" +#include "tier0/completion.h" +#include "engine/sys_utils.h" +#include "engine/net_chan.h" +#include "engine/baseclient.h" +#include "server/IVEngineServer.h" + +#ifndef DEDICATED +#include "gameui/IConsole.h" +#endif // !DEDICATED + +//----------------------------------------------------------------------------- +// Purpose: shutdown netchannel +//----------------------------------------------------------------------------- +void NET_ShutDown(void* thisptr, const char* szReason, std::uint8_t a1, char a2) +{ + DownloadPlaylists_Callback(); // Re-load playlist from disk after getting disconnected from the server. + NET_ShutDown(thisptr, szReason, a1, a2); +} + +//----------------------------------------------------------------------------- +// Purpose: hook and log the receive datagram +//----------------------------------------------------------------------------- +bool HNET_ReceiveDatagram(int iSocket, netpacket_s* pInpacket, bool bRaw) +{ + bool result = NET_ReceiveDatagram(iSocket, pInpacket, bRaw); + if (result) + { + // Log received packet data + HexDump("[+] NET_ReceiveDatagram", 0, &pInpacket->data[NULL], pInpacket->wiresize); + } + return result; +} + +//----------------------------------------------------------------------------- +// Purpose: hook and log the send datagram +//----------------------------------------------------------------------------- +void* HNET_SendDatagram(SOCKET s, const char* szPayload, int iLenght, int nFlags) +{ + void* result = NET_SendDatagram(s, szPayload, iLenght, nFlags); + if (result) + { + // Log transmitted packet data + HexDump("[+] NET_SendDatagram", 0, szPayload, iLenght); + } + return result; +} + +//----------------------------------------------------------------------------- +// Purpose: sets the user specified encryption key +//----------------------------------------------------------------------------- +void HNET_SetKey(std::string svNetKey) +{ + g_szNetKey.clear(); + g_szNetKey = svNetKey; + + DevMsg(eDLL_T::ENGINE, "______________________________________________________________\n"); + DevMsg(eDLL_T::ENGINE, "] NET_KEY ----------------------------------------------------\n"); + DevMsg(eDLL_T::ENGINE, "] BASE64: '%s'\n", g_szNetKey.c_str()); + DevMsg(eDLL_T::ENGINE, "--------------------------------------------------------------\n"); + + NET_SetKey(g_pNetKey, g_szNetKey.c_str()); +} + +//----------------------------------------------------------------------------- +// Purpose: calculates and sets the encryption key +//----------------------------------------------------------------------------- +void HNET_GenerateKey() +{ + g_szNetKey.clear(); + net_userandomkey->m_pParent->m_iValue = 1; + + BCRYPT_ALG_HANDLE hAlgorithm; + if (BCryptOpenAlgorithmProvider(&hAlgorithm, L"RNG", 0, 0) < 0) + { + DevMsg(eDLL_T::ENGINE, "Failed to open rng algorithm\n"); + return; + } + unsigned char pBuffer[0x10u]; + if (BCryptGenRandom(hAlgorithm, pBuffer, 0x10u, 0) < 0) + { + DevMsg(eDLL_T::ENGINE, "Failed to generate random data\n"); + return; + } + + for (int i = 0; i < 0x10u; i++) + { + g_szNetKey += pBuffer[i]; + } + + g_szNetKey = Base64Encode(g_szNetKey); + + DevMsg(eDLL_T::ENGINE, "______________________________________________________________\n"); + DevMsg(eDLL_T::ENGINE, "] NET_KEY ----------------------------------------------------\n"); + DevMsg(eDLL_T::ENGINE, "] BASE64: '%s'\n", g_szNetKey.c_str()); + DevMsg(eDLL_T::ENGINE, "--------------------------------------------------------------\n"); + + NET_SetKey(g_pNetKey, g_szNetKey.c_str()); +} + +//----------------------------------------------------------------------------- +// Purpose: hook and log the client's signonstate to the console +//----------------------------------------------------------------------------- +void HNET_PrintFunc(const char* fmt, ...) +{ + static char buf[1024]; + + va_list args; + va_start(args, fmt); + + vsnprintf(buf, sizeof(buf), fmt, args); + + buf[sizeof(buf) -1] = 0; + va_end(args); + + DevMsg(eDLL_T::CLIENT, "%s\n", buf); +} + +//----------------------------------------------------------------------------- +// Purpose: disconnect the client and shutdown netchannel +//----------------------------------------------------------------------------- +void NET_DisconnectClient(CClient* pClient, int nIndex, const char* szReason, uint8_t unk1, char unk2) +{ + if (!pClient) // Client valid? + { + return; + } + if (std::strlen(szReason) == NULL) // Is reason null? + { + return; + } + if (!pClient->GetNetChan()) + { + return; + } + + NET_Shutdown(pClient->GetNetChan(), szReason, unk1, unk2); // Shutdown netchan. + pClient->GetNetChan() = nullptr; // Null netchan. + CBaseClient_Clear((std::int64_t)pClient); // Reset CClient instance for client. + g_bIsPersistenceVarSet[nIndex] = false; // Reset Persistence var. +} + +/////////////////////////////////////////////////////////////////////////////// +void CNetChan_Attach() +{ + DetourAttach((LPVOID*)&NET_PrintFunc, &HNET_PrintFunc); +} + +void CNetChan_Detach() +{ + DetourDetach((LPVOID*)&NET_PrintFunc, &HNET_PrintFunc); +} + +void CNetChan_Trace_Attach() +{ + DetourAttach((LPVOID*)&NET_ReceiveDatagram, &HNET_ReceiveDatagram); + DetourAttach((LPVOID*)&NET_SendDatagram, &HNET_SendDatagram); +} + +void CNetChan_Trace_Detach() +{ + DetourDetach((LPVOID*)&NET_ReceiveDatagram, &HNET_ReceiveDatagram); + DetourDetach((LPVOID*)&NET_SendDatagram, &HNET_SendDatagram); +} + +/////////////////////////////////////////////////////////////////////////////// +std::string g_szNetKey = "WDNWLmJYQ2ZlM0VoTid3Yg=="; +std::uintptr_t g_pNetKey = g_mGameDll.StringSearch("client:NetEncryption_NewKey").FindPatternSelf("48 8D ? ? ? ? ? 48 3B", ADDRESS::Direction::UP, 150).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(); diff --git a/r5dev/engine/net_chan.h b/r5dev/engine/net_chan.h new file mode 100644 index 00000000..c3523431 --- /dev/null +++ b/r5dev/engine/net_chan.h @@ -0,0 +1,95 @@ +#pragma once +#include "core/stdafx.h" +#include "tier0/basetypes.h" +#include "common/protocol.h" +#include "client/client.h" + +typedef struct netpacket_s netpacket_t; +typedef struct __declspec(align(8)) netpacket_s +{ + DWORD family_maybe; + sockaddr_in sin; + WORD sin_port; + char gap16; + char byte17; + DWORD source; + double received; + std::uint8_t* data; + std::uint64_t label; + BYTE byte38; + std::uint64_t qword40; + std::uint64_t qword48; + BYTE gap50[8]; + std::uint64_t qword58; + std::uint64_t qword60; + std::uint64_t qword68; + int less_than_12; + DWORD wiresize; + BYTE gap78[8]; + struct netpacket_s *pNext; +} netpacket_t; + +namespace +{ + /* ==== CNETCHAN ======================================================================================================================================================== */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) + ADDRESS p_NET_Init = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x41\x54\x41\x56\x41\x57\x48\x81\xEC\xC0\x01\x00", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + void*(*NET_Init)(char a1) = (void* (*)(char))p_NET_Init.GetPtr(); /*48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 48 89 7C 24 20 41 54 41 56 41 57 48 81 EC C0 01 00*/ + + ADDRESS p_NET_Shutdown = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x6C\x24\x18\x56\x57\x41\x56\x48\x83\xEC\x30\x83\xB9\xD8", "xxxxxxxxxxxxxxxx"); + void (*NET_Shutdown)(void* thisptr, const char* a0, std::uint8_t a1, char a2) = (void (*)(void*, const char*, std::uint8_t, char))p_NET_Shutdown.GetPtr(); /*48 89 6C 24 18 56 57 41 56 48 83 EC 30 83 B9 D8*/ +#elif defined (GAMEDLL_S3) + ADDRESS p_NET_Init = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x41\x54\x41\x56\x41\x57\x48\x81\xEC\xF0\x01\x00", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + void* (*NET_Init)(char a1) = (void* (*)(char))p_NET_Init.GetPtr(); /*48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 48 89 7C 24 20 41 54 41 56 41 57 48 81 EC F0 01 00*/ + + ADDRESS p_NET_Shutdown = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x6C\x24\x18\x56\x57\x41\x56\x48\x83\xEC\x30\x83\xB9\xD0", "xxxxxxxxxxxxxxxx"); + void (*NET_Shutdown)(void* thisptr, const char* szReason, std::uint8_t a1, char a2) = (void (*)(void*, const char*, std::uint8_t, char))p_NET_Shutdown.GetPtr(); /*48 89 6C 24 18 56 57 41 56 48 83 EC 30 83 B9 D0*/ +#endif + ADDRESS p_NET_SetKey = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x57\x48\x83\xEC\x20\x48\x8B\xF9\x41\xB8", "xxxxxxxxxxxxxxxxxxxxxxxxx"); + void (*NET_SetKey)(std::uintptr_t pKey, const char* szHash) = (void (*)(std::uintptr_t, const char*))p_NET_SetKey.GetPtr(); /*48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 48 8B F9 41 B8*/ + + ADDRESS p_NET_ReceiveDatagram = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\xAC\x24\x50\xEB", "xxxxxxxxxxxxxxxxxxxxxxxxx"); + bool (*NET_ReceiveDatagram)(int iSocket, netpacket_s* pInpacket, bool bRaw) = (bool (*)(int, netpacket_s*, bool))p_NET_ReceiveDatagram.GetPtr(); /*E8 ?? ?? ?? ?? 84 C0 75 35 48 8B D3*/ + + ADDRESS p_NET_SendDatagram = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x57\x41\x56\x41\x57\x48\x81\xEC\x00\x05\x00\x00", "xxxxxxxxxxxxxxxxxxxxxxx?xxx"); + void*(*NET_SendDatagram)(SOCKET s, const char* szPayload, int iLenght, int nFlags) = (void*(*)(SOCKET, const char*, int, int))p_NET_SendDatagram.GetPtr(); /*48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ?? 05 00 00*/ + + ADDRESS p_NET_PrintFunc = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x54\x24\x10\x4C\x89\x44\x24\x18\x4C\x89\x4C\x24\x20\xC3\x48", "xxxxxxxxxxxxxxxxx"); + void (*NET_PrintFunc)(const char* fmt) = (void(*)(const char*))p_NET_PrintFunc.GetPtr(); /*48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 C3 48*/ +} + +/////////////////////////////////////////////////////////////////////////////// +bool HNET_ReceiveDatagram(int iSocket, netpacket_s* pInpacket, bool bRaw); +void* HNET_SendDatagram(SOCKET s, const char* szPayload, int iLenght, int nFlags); +void HNET_SetKey(std::string svNetKey); +void HNET_GenerateKey(); +void HNET_PrintFunc(const char* fmt, ...); +void NET_DisconnectClient(CClient* pClient, int nIndex, const char* szReason, std::uint8_t unk1, char unk2); + +void CNetChan_Attach(); +void CNetChan_Detach(); +void CNetChan_Trace_Attach(); +void CNetChan_Trace_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +extern std::string g_szNetKey; +extern std::uintptr_t g_pNetKey; + +/////////////////////////////////////////////////////////////////////////////// +class HNetChan : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: NET_Init : 0x" << std::hex << std::uppercase << p_NET_Init.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: NET_Shutdown : 0x" << std::hex << std::uppercase << p_NET_Shutdown.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: NET_SetKey : 0x" << std::hex << std::uppercase << p_NET_SetKey.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: NET_ReceiveDatagram : 0x" << std::hex << std::uppercase << p_NET_ReceiveDatagram.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: NET_SendDatagram : 0x" << std::hex << std::uppercase << p_NET_SendDatagram.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: NET_PrintFunc : 0x" << std::hex << std::uppercase << p_NET_PrintFunc.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pNetKey : 0x" << std::hex << std::uppercase << g_pNetKey << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HNetChan); diff --git a/r5dev/engine/sys_dll.cpp b/r5dev/engine/sys_dll.cpp new file mode 100644 index 00000000..d22d7f43 --- /dev/null +++ b/r5dev/engine/sys_dll.cpp @@ -0,0 +1,26 @@ +#include "core/stdafx.h" +#include "engine/sys_dll.h" + +//----------------------------------------------------------------------------- +// Sys_Error_Internal +// +//----------------------------------------------------------------------------- +int HSys_Error_Internal(char* fmt, va_list args) +{ + printf("\n_____________________________________________________________\n"); + printf("] ENGINE ERROR ################################################\n"); + vprintf(fmt, args); + + /////////////////////////////////////////////////////////////////////////// + return Sys_Error_Internal(fmt, args); +} + +void SysDll_Attach() +{ + DetourAttach((LPVOID*)&Sys_Error_Internal, &HSys_Error_Internal); +} + +void SysDll_Detach() +{ + DetourDetach((LPVOID*)&Sys_Error_Internal, &HSys_Error_Internal); +} diff --git a/r5dev/engine/sys_dll.h b/r5dev/engine/sys_dll.h new file mode 100644 index 00000000..eaa4df4d --- /dev/null +++ b/r5dev/engine/sys_dll.h @@ -0,0 +1,27 @@ +#pragma once + +namespace +{ + /* ==== UTILITY ========================================================================================================================================================= */ + ADDRESS p_Sys_Error_Internal = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x48\x89\x74\x24\x10\x57\x48\x81\xEC\x30\x08\x00\x00\x48\x8B\xDA\x48\x8B\xF9\xE8\x00\x00\x00\xFF\x33\xF6\x48", "xxxxxxxxxxxxxxxxxxxxxxxxx???xxxx"); + int (*Sys_Error_Internal)(char* fmt, va_list args) = (int (*)(char*, va_list))p_Sys_Error_Internal.GetPtr(); /*48 89 5C 24 08 48 89 74 24 10 57 48 81 EC 30 08 00 00 48 8B DA 48 8B F9 E8 ?? ?? ?? FF 33 F6 48*/ +} + +/////////////////////////////////////////////////////////////////////////////// +int HSys_Error_Internal(char* fmt, va_list args); + +void SysDll_Attach(); +void SysDll_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HSys_Dll : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: Sys_Error_Internal : 0x" << std::hex << std::uppercase << p_Sys_Error_Internal.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HSys_Dll); \ No newline at end of file diff --git a/r5dev/engine/sys_dll2.cpp b/r5dev/engine/sys_dll2.cpp new file mode 100644 index 00000000..03f87c71 --- /dev/null +++ b/r5dev/engine/sys_dll2.cpp @@ -0,0 +1,3 @@ +#include "core/stdafx.h" +#include "engine/sys_dll.h" +#include "engine/sys_dll2.h" diff --git a/r5dev/engine/sys_dll2.h b/r5dev/engine/sys_dll2.h new file mode 100644 index 00000000..eedac0c1 --- /dev/null +++ b/r5dev/engine/sys_dll2.h @@ -0,0 +1,40 @@ +#pragma once +#include "tier0/basetypes.h" +#include "tier0/interface.h" + +class CEngineAPI +{ + // TODO [ AMOS ]: +}; + +namespace +{ + ADDRESS p_CEngineAPI_Connect = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x48\x8B\x05\x00\x00\x00\x00\x48\x8D\x0D\x00\x00\x00\x00\x48\x85\xC0\x48\x89\x15", "xxxxxxx????xxx????xxxxxx"); + bool (*CEngineAPI_Connect)(CEngineAPI* thisptr, CreateInterfaceFn factory) = (bool (*)(CEngineAPI*, CreateInterfaceFn))p_CEngineAPI_Connect.GetPtr(); /*48 83 EC 28 48 8B 05 ? ? ? ? 48 8D 0D ? ? ? ? 48 85 C0 48 89 15 ? ? ? ?*/ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_PakFile_Init = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x6C\x24\x00\x44\x88\x44\x24\x00\x56\x57\x41\x54\x41\x56\x41\x57\x48\x83\xEC\x20", "xxxx?xxxx?xxxx?xxxxxxxxxxxx"); + int (*PakFile_Init)(char* buffer, char* source, char vpk_file) = (int (*)(char*, char*, char))p_PakFile_Init.GetPtr(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 44 88 44 24 ?? 56 57 41 54 41 56 41 57 48 83 EC 20*/ + + ADDRESS g_pMapVPKCache = p_PakFile_Init.FindPatternSelf("4C 8D 35 ?? ?? ?? ?? 44", ADDRESS::Direction::DOWN, 250).OffsetSelf(0x3).ResolveRelativeAddressSelf().GetPtr(); +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_PakFile_Init = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x44\x88\x44\x24\x00\x53\x55\x56\x57", "xxxx?xxxx"); + void (*PakFile_Init)(char* buffer, char* source, char vpk_file) = (void (*)(char*, char*, char))p_PakFile_Init.GetPtr(); /*44 88 44 24 ?? 53 55 56 57*/ + + ADDRESS g_pMapVPKCache = p_PakFile_Init.FindPatternSelf("48 8D 1D ?? ?? ?? ?? 4C", ADDRESS::Direction::DOWN, 250).OffsetSelf(0x3).ResolveRelativeAddressSelf().GetPtr(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +class HSys_Dll2 : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CEngineAPI_Connect : 0x" << std::hex << std::uppercase << p_CEngineAPI_Connect.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: PakFile_Init : 0x" << std::hex << std::uppercase << p_PakFile_Init.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pMapVPKCache : 0x" << std::hex << std::uppercase << g_pMapVPKCache.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HSys_Dll2); \ No newline at end of file diff --git a/r5dev/engine/sys_utils.cpp b/r5dev/engine/sys_utils.cpp new file mode 100644 index 00000000..85c136f3 --- /dev/null +++ b/r5dev/engine/sys_utils.cpp @@ -0,0 +1,123 @@ +#include "core/stdafx.h" +#include "core/logdef.h" +#include "engine/sys_utils.h" +#include "vgui/CEngineVGui.h" +#include "gameui/IConsole.h" + +//----------------------------------------------------------------------------- +// Sys_Error +// +//----------------------------------------------------------------------------- +void HSys_Error(char* fmt, ...) +{ + static char buf[1024]; + + va_list args; + va_start(args, fmt); + + vsnprintf(buf, sizeof(buf), fmt, args); + + buf[sizeof(buf) -1] = 0; + va_end(args); + + DevMsg(eDLL_T::ENGINE, "%s\n", buf); + Sys_Error(buf); +} + +//----------------------------------------------------------------------------- +// Sys_Print +// +//----------------------------------------------------------------------------- +void DevMsg(eDLL_T idx, const char* fmt, ...) +{ + int vmIdx = (int)idx; + static bool initialized = false; + static char buf[1024]; + + static std::string vmType[7] = { "Native(S):", "Native(C):", "Native(U):", "Native(E):", "Native(F):", "Native(R):", "Native(M):" }; + + static auto iconsole = spdlog::stdout_logger_mt("sys_print_iconsole"); // in-game console. + static auto wconsole = spdlog::stdout_logger_mt("sys_print_wconsole"); // windows console. + static auto sqlogger = spdlog::basic_logger_mt("sys_print_logger", "platform\\logs\\sys_print.log"); // file logger. + + std::string vmStr = vmType[vmIdx].c_str(); + + g_spd_sys_w_oss.str(""); + g_spd_sys_w_oss.clear(); + + if (!initialized) + { + iconsole = std::make_shared("sys_print_ostream", g_spd_sys_p_ostream_sink); + iconsole->set_pattern("[%S.%e] %v"); + iconsole->set_level(spdlog::level::debug); + wconsole->set_pattern("[%S.%e] %v"); + wconsole->set_level(spdlog::level::debug); + sqlogger->set_pattern("[%S.%e] %v"); + sqlogger->set_level(spdlog::level::debug); + initialized = true; + } + + va_list args; + va_start(args, fmt); + + vsnprintf(buf, sizeof(buf), fmt, args); + + buf[sizeof(buf) - 1] = 0; + va_end(args); + + vmStr.append(buf); + + iconsole->debug(vmStr); + wconsole->debug(vmStr); + sqlogger->debug(vmStr); + +#ifndef DEDICATED + std::string s = g_spd_sys_w_oss.str(); + const char* c = s.c_str(); + + g_pLogSystem.AddLog((LogType_t)eDLL_T::ENGINE, s); + Items.push_back(Strdup((const char*)c)); +#endif // !DEDICATED +} + +//----------------------------------------------------------------------------- +// Sys_LoadAssetHelper +// +//----------------------------------------------------------------------------- +void* HSys_LoadAssetHelper(const CHAR* lpFileName, std::int64_t a2, LARGE_INTEGER* a3) +{ + std::string mod_file; + std::string base_file = lpFileName; + const std::string mod_dir = "paks\\Win32\\"; + const std::string base_dir = "paks\\Win64\\"; + + if (strstr(lpFileName, base_dir.c_str())) + { + base_file.erase(0, 11); // Erase 'base_dir'. + mod_file = mod_dir + base_file; // Prepend 'mod_dir'. + + if (FileExists(mod_file.c_str())) + { + // Load decompressed pak files from 'mod_dir'. + DevMsg(eDLL_T::RTECH, "Loading pak: '%s'\n", mod_file.c_str()); + return Sys_LoadAssetHelper(mod_file.c_str(), a2, a3); + } + } + if (strstr(lpFileName, base_dir.c_str())) + { + DevMsg(eDLL_T::RTECH, "Loading pak: '%s'\n", lpFileName); + } + return Sys_LoadAssetHelper(lpFileName, a2, a3); +} + +void SysUtils_Attach() +{ + DetourAttach((LPVOID*)&Sys_Error, &HSys_Error); + DetourAttach((LPVOID*)&Sys_LoadAssetHelper, &HSys_LoadAssetHelper); +} + +void SysUtils_Detach() +{ + DetourDetach((LPVOID*)&Sys_Error, &HSys_Error); + DetourDetach((LPVOID*)&Sys_LoadAssetHelper, &HSys_LoadAssetHelper); +} diff --git a/r5dev/engine/sys_utils.h b/r5dev/engine/sys_utils.h new file mode 100644 index 00000000..5bc41fb2 --- /dev/null +++ b/r5dev/engine/sys_utils.h @@ -0,0 +1,53 @@ +#pragma once +#include "tier0/basetypes.h" + +namespace +{ + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ADDRESS p_Sys_Error = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x4C\x24\x08\x48\x89\x54\x24\x10\x4C\x89\x44\x24\x18\x4C\x89\x4C\x24\x20\x53\x55\x41\x54\x41\x56\xB8\x58\x10\x00\x00\xE8", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + void (*Sys_Error)(char* fmt, ...) = (void (*)(char* fmt, ...))p_Sys_Error.GetPtr(); /*48 89 4C 24 08 48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 53 55 41 54 41 56 B8 58 10 00 00 E8*/ + + ADDRESS p_Sys_LoadAssetHelper = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x74\x24\x10\x48\x89\x7C\x24\x18\x41\x56\x48\x83\xEC\x40\x33", "xxxxxxxxxxxxxxxxx"); + void*(*Sys_LoadAssetHelper)(const CHAR* lpFileName, std::int64_t a2, LARGE_INTEGER* a3) = (void*(*)(const CHAR*, std::int64_t, LARGE_INTEGER*))p_Sys_LoadAssetHelper.GetPtr();/*48 89 74 24 10 48 89 7C 24 18 41 56 48 83 EC 40 33*/ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) + ADDRESS p_MemAlloc_Wrapper = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x48\x8B\x05\x00\x00\x00\x00\x48\x8B\xD9\x48\x85\xC0\x75\x0C\xE8\x16", "xxxxxxxxx????xxxxxxxxxx"); + void* (*MemAlloc_Wrapper)(std::int64_t size) = (void* (*)(std::int64_t))p_MemAlloc_Wrapper.GetPtr(); /*40 53 48 83 EC 20 48 8B 05 ?? ?? ?? ?? 48 8B D9 48 85 C0 75 0C E8 16*/ +#elif defined (GAMEDLL_S3) + ADDRESS p_MemAlloc_Wrapper = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x48\x8B\x05\x6B\x83\x25\x0D\x48\x8B\xD9", "xxxxxxxxxxxxxxxx"); + void* (*MemAlloc_Wrapper)(std::int64_t size) = (void* (*)(std::int64_t))p_MemAlloc_Wrapper.GetPtr(); /*40 53 48 83 EC 20 48 8B 05 6B 83 25 0D 48 8B D9*/ +#endif + /* ==== ------- ========================================================================================================================================================= */ +} + +enum class eDLL_T : int +{ + SERVER = 0, // Game DLL + CLIENT = 1, // Game DLL + UI = 2, // Game DLL + ENGINE = 3, // Wrapper + FS = 4, // File System + RTECH = 5, // RTech API + MS = 6 // Material System +}; + +/////////////////////////////////////////////////////////////////////////////// +void HSys_Error(char* fmt, ...); +void DevMsg(eDLL_T idx, const char* fmt, ...); + +void SysUtils_Attach(); +void SysUtils_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HSys_Utils : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: Sys_Error : 0x" << std::hex << std::uppercase << p_Sys_Error.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: Sys_LoadAssetHelper : 0x" << std::hex << std::uppercase << p_Sys_LoadAssetHelper.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: MemAlloc_Wrapper : 0x" << std::hex << std::uppercase << p_MemAlloc_Wrapper.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HSys_Utils); diff --git a/r5dev/gameui/IBrowser.cpp b/r5dev/gameui/IBrowser.cpp new file mode 100644 index 00000000..2cb83b5a --- /dev/null +++ b/r5dev/gameui/IBrowser.cpp @@ -0,0 +1,712 @@ +#include "core/stdafx.h" +#include "core/init.h" +#include "core/resource.h" +#include "tier0/IConVar.h" +#include "tier0/cvar.h" +#include "tier0/completion.h" +#include "windows/id3dx.h" +#include "windows/console.h" +#include "engine/net_chan.h" +#include "engine/sys_utils.h" +#include "engine/host_state.h" +#include "server/server.h" +#include "client/IVEngineClient.h" +#include "networksystem/serverlisting.h" +#include "networksystem/r5net.h" +#include "vpc/keyvalues.h" +#include "squirrel/sqinit.h" +#include "gameui/IBrowser.h" + +/****************************************************************************** +------------------------------------------------------------------------------- +File : IBrowser.cpp +Date : 09:06:2021 +Author : Sal +Purpose: Implements the in-game server browser frontend +------------------------------------------------------------------------------- +History: +- 09:06:2021 21:07 : Created by Sal +- 25:07:2021 14:26 : Implement private servers connect dialog and password field + +******************************************************************************/ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +IBrowser::IBrowser() +{ + memset(m_chServerConnStringBuffer, 0, sizeof(m_chServerConnStringBuffer)); + + std::string path = "stbsp"; + for (const auto& entry : std::filesystem::directory_iterator(path)) + { + std::string filename = entry.path().string(); + int slashPos = filename.rfind("\\", std::string::npos); + filename = filename.substr(static_cast, std::allocator>::size_type>(slashPos) + 1, std::string::npos); + filename = filename.substr(0, filename.size() - 6); + + auto it = mapArray.find(filename); // Find MapName in mapArray. + if (it != mapArray.end()) + { + m_vszMapsList.push_back(it->second); + } + else + { + m_vszMapsList.push_back(filename); + } + } + + m_szMatchmakingHostName = r5net_matchmaking_hostname->m_pzsCurrentValue; + static std::thread hostingServerRequestThread([this]() + { + while (true) + { + UpdateHostingStatus(); + std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + } + }); + + hostingServerRequestThread.detach(); + + /* Obtain handle to module */ + static HGLOBAL rcData = NULL; + HMODULE handle; + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)"unnamed", &handle); + HRSRC rc = FindResource(handle, MAKEINTRESOURCE(IDB_PNG1), MAKEINTRESOURCE(PNG)); + /* Obtain assets from 'rsrc' */ + if (rc != NULL) + { rcData = LoadResource(handle, rc); } + else { assert(rc == NULL); } + if (rcData != NULL) { m_vucLockedIconBlob = (std::vector*)LockResource(rcData); } + else { assert(rcData == NULL); } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +IBrowser::~IBrowser() +{ + //delete r5net; +} + +//----------------------------------------------------------------------------- +// Purpose: updates the hoster's status +//----------------------------------------------------------------------------- +void IBrowser::UpdateHostingStatus() +{ + if (!g_pHostState || !g_pCvar) + { + return; + } + + eHostingStatus = g_pHostState->m_bActiveGame ? EHostStatus::HOSTING : EHostStatus::NOT_HOSTING; // Are we hosting a server? + switch (eHostingStatus) + { + case EHostStatus::NOT_HOSTING: + { + m_szHostRequestMessage.clear(); + m_iv4HostRequestMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + break; + } + case EHostStatus::HOSTING: + { + if (eServerVisibility == EServerVisibility::OFFLINE) + { + break; + } + + if (*g_nRemoteFunctionCallsChecksum == NULL) // Check if script checksum is valid yet. + { + break; + } + + switch (eServerVisibility) + { + + case EServerVisibility::HIDDEN: + m_Server.bHidden = true; + break; + case EServerVisibility::PUBLIC: + m_Server.bHidden = false; + break; + default: + break; + } + + SendHostingPostRequest(); + break; + } + default: + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: refreshes the server browser list with available servers +//----------------------------------------------------------------------------- +void IBrowser::RefreshServerList() +{ + static bool bThreadLocked = false; + + m_vServerList.clear(); + m_szServerListMessage.clear(); + + if (!bThreadLocked) + { + std::thread t([this]() + { + DevMsg(eDLL_T::CLIENT, "Refreshing server list with string '%s'\n", r5net_matchmaking_hostname->m_pzsCurrentValue); + bThreadLocked = true; + m_vServerList = g_pR5net->GetServersList(m_szServerListMessage); + bThreadLocked = false; + }); + + t.detach(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: sends the hosting POST request to the comp server +//----------------------------------------------------------------------------- +void IBrowser::SendHostingPostRequest() +{ + m_szHostToken = std::string(); + DevMsg(eDLL_T::CLIENT, "Sending PostServerHost request\n"); + bool result = g_pR5net->PostServerHost(m_szHostRequestMessage, m_szHostToken, + ServerListing{ + m_Server.svServerName, + std::string(g_pHostState->m_levelName), + "", + g_pCvar->FindVar("hostport")->m_pzsCurrentValue, + g_pCvar->FindVar("mp_gamemode")->m_pzsCurrentValue, + m_Server.bHidden, + std::to_string(*g_nRemoteFunctionCallsChecksum), + + std::string(), + g_szNetKey.c_str() + } + ); + + if (result) + { + m_iv4HostRequestMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); + std::stringstream msg; + msg << "Broadcasting! "; + if (!m_szHostToken.empty()) + { + msg << "Share the following token for clients to connect: "; + } + m_szHostRequestMessage = msg.str().c_str(); + } + else + { + m_iv4HostRequestMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + } +} + +//----------------------------------------------------------------------------- +// Purpose: draws the compmenu +//----------------------------------------------------------------------------- +void IBrowser::CompMenu() +{ + ImGui::BeginTabBar("CompMenu"); + if (ImGui::TabItemButton("Server Browser")) + { + SetSection(ESection::SERVER_BROWSER); + } + if (ImGui::TabItemButton("Host Server")) + { + SetSection(ESection::HOST_SERVER); + } + if (ImGui::TabItemButton("Settings")) + { + SetSection(ESection::SETTINGS); + } + ImGui::EndTabBar(); +} + +//----------------------------------------------------------------------------- +// Purpose: draws the server browser section +//----------------------------------------------------------------------------- +void IBrowser::ServerBrowserSection() +{ + ImGui::BeginGroup(); + m_imServerBrowserFilter.Draw(); + ImGui::SameLine(); + if (ImGui::Button("Refresh List")) + { + RefreshServerList(); + } + ImGui::EndGroup(); + ImGui::TextColored(ImVec4(1.00f, 0.00f, 0.00f, 1.00f), m_szServerListMessage.c_str()); + ImGui::Separator(); + + const float FooterHeight = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); + ImGui::BeginChild("ServerListChild", { 0, -FooterHeight }, true, ImGuiWindowFlags_AlwaysVerticalScrollbar); + if (ImGui::BeginTable("##ServerBrowser_ServerList", 5, ImGuiTableFlags_Resizable)) + { + ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch, 20); + ImGui::TableSetupColumn("Map", ImGuiTableColumnFlags_WidthStretch, 25); + ImGui::TableSetupColumn("Port", ImGuiTableColumnFlags_WidthStretch, 5); + ImGui::TableSetupColumn("Playlist", ImGuiTableColumnFlags_WidthStretch, 5); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 5); + ImGui::TableHeadersRow(); + + for (ServerListing& server : m_vServerList) + { + const char* name = server.svServerName.c_str(); + const char* map = server.svMapName.c_str(); + const char* port = server.svPort.c_str(); + const char* playlist = server.svPlaylist.c_str(); + + if (m_imServerBrowserFilter.PassFilter(name) + || m_imServerBrowserFilter.PassFilter(map) + || m_imServerBrowserFilter.PassFilter(port)) + { + ImGui::TableNextColumn(); + ImGui::Text(name); + + ImGui::TableNextColumn(); + ImGui::Text(map); + + ImGui::TableNextColumn(); + ImGui::Text(port); + + ImGui::TableNextColumn(); + ImGui::Text(playlist); + + ImGui::TableNextColumn(); + std::string selectButtonText = "Connect##"; + selectButtonText += (server.svServerName + server.svIpAddress + server.svMapName); + + if (ImGui::Button(selectButtonText.c_str())) + { + ConnectToServer(server.svIpAddress, server.svPort, server.svEncryptionKey); + } + } + + } + ImGui::EndTable(); + } + ImGui::EndChild(); + + ImGui::Separator(); + ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth() / 4); + { + ImGui::InputTextWithHint("##ServerBrowser_ServerConnString", "Enter IP address or \"localhost\"", m_chServerConnStringBuffer, IM_ARRAYSIZE(m_chServerConnStringBuffer)); + + ImGui::SameLine(); + ImGui::InputTextWithHint("##ServerBrowser_ServerEncKey", "Enter encryption key", m_chServerEncKeyBuffer, IM_ARRAYSIZE(m_chServerEncKeyBuffer)); + + ImGui::SameLine(); + if (ImGui::Button("Connect##ServerBrowser_ConnectByIp", ImVec2(ImGui::GetWindowContentRegionWidth() / 4.2, 18.5))) + { + ConnectToServer(m_chServerConnStringBuffer, m_chServerEncKeyBuffer); + } + + ImGui::SameLine(); + if (ImGui::Button("Private Servers##ServerBrowser_HiddenServersButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 4.2, 18.5))) + { + ImGui::OpenPopup("Connect to Private Server##HiddenServersConnectModal"); + } + HiddenServersModal(); + } + ImGui::PopItemWidth(); +} + +//----------------------------------------------------------------------------- +// Purpose: draws the hidden private server modal +//----------------------------------------------------------------------------- +void IBrowser::HiddenServersModal() +{ + bool modalOpen = true; + if (ImGui::BeginPopupModal("Connect to Private Server##HiddenServersConnectModal", &modalOpen)) + { + ImGui::SetWindowSize(ImVec2(400.f, 200.f), ImGuiCond_Always); + + if (!m_idLockedIcon) + { + bool ret = LoadTextureBuffer((unsigned char*)m_vucLockedIconBlob, 0x1000 /*TODO [ AMOS ]: Calculate size dynamically*/, &m_idLockedIcon, &m_nLockedIconWidth, &m_nLockedIconHeight); + IM_ASSERT(ret); + } + + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); // Override the style color for child bg. + + ImGui::BeginChild("##HiddenServersConnectModal_IconParent", ImVec2(m_nLockedIconWidth, m_nLockedIconHeight)); + ImGui::Image(m_idLockedIcon, ImVec2(m_nLockedIconWidth, m_nLockedIconHeight)); // Display texture. + ImGui::EndChild(); + + ImGui::PopStyleColor(); // Pop the override for the child bg. + + ImGui::SameLine(); + ImGui::Text("Enter the token to connect"); + + ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth()); // Override item width. + ImGui::InputTextWithHint("##HiddenServersConnectModal_TokenInput", "Token", &m_szHiddenServerToken); + ImGui::PopItemWidth(); + + ImGui::Dummy(ImVec2(ImGui::GetWindowContentRegionWidth(), 19.f)); // Place a dummy, basically making space inserting a blank element. + + ImGui::TextColored(m_ivHiddenServerMessageColor, m_szHiddenServerRequestMessage.c_str()); + ImGui::Separator(); + + if (ImGui::Button("Connect##HiddenServersConnectModal_ConnectButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2, 24))) + { + m_szHiddenServerRequestMessage.clear(); + ServerListing server; + bool result = g_pR5net->GetServerByToken(server, m_szHiddenServerRequestMessage, m_szHiddenServerToken); // Send token connect request. + if (!server.svServerName.empty()) + { + ConnectToServer(server.svIpAddress, server.svPort, server.svEncryptionKey); // Connect to the server + m_szHiddenServerRequestMessage = "Found Server: " + server.svServerName; + m_ivHiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); + ImGui::CloseCurrentPopup(); + } + else + { + m_szHiddenServerRequestMessage = "Error: " + m_szHiddenServerRequestMessage; + m_ivHiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + } + } + + ImGui::SameLine(); + if (ImGui::Button("Close##HiddenServersConnectModal_CloseButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2, 24))) + { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: draws the host section +//----------------------------------------------------------------------------- +void IBrowser::HostServerSection() +{ + static std::string szServerNameErr = ""; + + ImGui::InputTextWithHint("##ServerHost_ServerName", "Server Name (Required)", &m_Server.svServerName); + ImGui::Spacing(); + + if (ImGui::BeginCombo("Playlist##ServerHost_PlaylistBox", m_Server.svPlaylist.c_str())) + { + for (auto& item : g_szAllPlaylists) + { + if (ImGui::Selectable(item.c_str(), item == m_Server.svPlaylist)) + { + m_Server.svPlaylist = item; + } + } + ImGui::EndCombo(); + } + + if (ImGui::BeginCombo("Map##ServerHost_MapListBox", m_Server.svMapName.c_str())) + { + for (auto& item : m_vszMapsList) + { + if (ImGui::Selectable(item.c_str(), item == m_Server.svMapName)) + { + m_Server.svMapName = item; + for (auto it = mapArray.begin(); it != mapArray.end(); ++it) + { + if (it->second.compare(m_Server.svMapName) == NULL) + { + m_Server.svMapName = it->first; + } + } + } + } + ImGui::EndCombo(); + } + + ImGui::Checkbox("Load Global Ban List##ServerHost_CheckCompBanDBCheckbox", &g_bCheckCompBanDB); + ImGui::Spacing(); + + ImGui::SameLine(); + ImGui::Text("Server Visiblity"); + + if (ImGui::SameLine(); ImGui::RadioButton("Offline##ServerHost_ServerChoice1", eServerVisibility == EServerVisibility::OFFLINE)) + { + eServerVisibility = EServerVisibility::OFFLINE; + } + if (ImGui::SameLine(); ImGui::RadioButton("Hidden##ServerHost_ServerChoice2", eServerVisibility == EServerVisibility::HIDDEN)) + { + eServerVisibility = EServerVisibility::HIDDEN; + } + if (ImGui::SameLine(); ImGui::RadioButton("Public##ServerHost_ServerChoice2", eServerVisibility == EServerVisibility::PUBLIC)) + { + eServerVisibility = EServerVisibility::PUBLIC; + } + + ImGui::Spacing(); + ImGui::Separator(); + + if (!g_pHostState->m_bActiveGame) + { + if (ImGui::Button("Start Server##ServerHost_StartServerButton", ImVec2(ImGui::GetWindowSize().x, 32))) + { + szServerNameErr.clear(); + if (!m_Server.svServerName.empty() && !m_Server.svPlaylist.empty() && !m_Server.svMapName.empty()) + { + DevMsg(eDLL_T::ENGINE, "Starting Server with name '%s', map '%s' and playlist '%s'\n", m_Server.svServerName.c_str(), m_Server.svMapName.c_str(), m_Server.svPlaylist.c_str()); + szServerNameErr = std::string(); + UpdateHostingStatus(); + + /* + * Playlist gets parsed in two instances, first in LoadPlaylist all the neccessary values. + * Then when you would normally call launchplaylist which calls StartPlaylist it would cmd call mp_gamemode which parses the gamemode specific part of the playlist.. + */ + KeyValues_LoadPlaylist(m_Server.svPlaylist.c_str()); + std::stringstream cgmd; + cgmd << "mp_gamemode " << m_Server.svPlaylist; + ProcessCommand(cgmd.str().c_str()); + + // This is to avoid a race condition. + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + std::stringstream cmd; + cmd << "map " << m_Server.svMapName; + ProcessCommand(cmd.str().c_str()); + } + else + { + if (m_Server.svServerName.empty()) + { + szServerNameErr = "No Server Name assigned."; + } + else if (m_Server.svPlaylist.empty()) + { + szServerNameErr = "No Playlist assigned."; + } + else if (m_Server.svMapName.empty()) + { + szServerNameErr = "'levelname' was empty."; + } + } + } + } + + if (ImGui::Button("Force Start##ServerHost_ForceStart", ImVec2(ImGui::GetWindowSize().x, 32))) + { + szServerNameErr.clear(); + if (!m_Server.svPlaylist.empty() && !m_Server.svMapName.empty()) + { + DevMsg(eDLL_T::ENGINE, "Starting Server with map '%s' and playlist '%s'\n", m_Server.svMapName.c_str(), m_Server.svPlaylist.c_str()); + szServerNameErr = std::string(); + UpdateHostingStatus(); + + /* + * Playlist gets parsed in two instances, first in LoadPlaylist all the neccessary values. + * Then when you would normally call launchplaylist which calls StartPlaylist it would cmd call mp_gamemode which parses the gamemode specific part of the playlist.. + */ + KeyValues_LoadPlaylist(m_Server.svPlaylist.c_str()); + std::stringstream cgmd; + cgmd << "mp_gamemode " << m_Server.svPlaylist; + ProcessCommand(cgmd.str().c_str()); + + // This is to avoid a race condition. + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + std::stringstream cmd; + cmd << "map " << m_Server.svMapName; + ProcessCommand(cmd.str().c_str()); + } + else + { + if (m_Server.svPlaylist.empty()) + { + szServerNameErr = "No Playlist assigned."; + } + else if (m_Server.svMapName.empty()) + { + szServerNameErr = "'levelname' was empty."; + } + } + } + + ImGui::TextColored(ImVec4(1.00f, 0.00f, 0.00f, 1.00f), szServerNameErr.c_str()); + ImGui::TextColored(m_iv4HostRequestMessageColor, m_szHostRequestMessage.c_str()); + if (!m_szHostToken.empty()) + { + ImGui::InputText("##ServerHost_HostToken", &m_szHostToken, ImGuiInputTextFlags_ReadOnly); + } + + if (g_pHostState->m_bActiveGame) + { + if (ImGui::Button("Reload Scripts##ServerHost_ReloadServerButton", ImVec2(ImGui::GetWindowSize().x, 32))) + { + DevMsg(eDLL_T::ENGINE, "Recompiling scripts\n"); + ProcessCommand("reparse_weapons"); + ProcessCommand("reload"); + } + + if (ImGui::Button("Change Level##ServerHost_ChangeLevel", ImVec2(ImGui::GetWindowSize().x, 32))) + { + if (!m_Server.svMapName.empty()) + { + strncpy_s(g_pHostState->m_levelName, m_Server.svMapName.c_str(), 64); // Copy new map into hoststate levelname. 64 is size of m_levelname. + g_pHostState->m_iNextState = HostStates_t::HS_CHANGE_LEVEL_MP; // Force CHostState::FrameUpdate to change the level. + } + else + { + szServerNameErr = "Failed to change level: 'levelname' was empty."; + } + } + + if (ImGui::Button("Stop Server##ServerHost_StopServerButton", ImVec2(ImGui::GetWindowSize().x, 32))) + { + ProcessCommand("LeaveMatch"); // TODO: use script callback instead. + g_pHostState->m_iNextState = HostStates_t::HS_GAME_SHUTDOWN; // Force CHostState::FrameUpdate to shutdown the server for dedicated. + } + } + else + { + if (ImGui::Button("Reload Playlist from Disk##ServerHost_ReloadPlaylist", ImVec2(ImGui::GetWindowSize().x, 32))) + { + DownloadPlaylists_Callback(); + CKeyValueSystem_InitPlaylist(); // Re-Init playlist. + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: draws the settings section +//----------------------------------------------------------------------------- +void IBrowser::SettingsSection() +{ + ImGui::InputTextWithHint("Hostname##MatchmakingServerString", "Matchmaking Server String", &m_szMatchmakingHostName); + if (ImGui::Button("Update Hostname")) + { + r5net_matchmaking_hostname->m_pzsCurrentValue = m_szMatchmakingHostName.c_str(); + if (g_pR5net) + { + delete g_pR5net; + g_pR5net = new R5Net::Client(r5net_matchmaking_hostname->m_pzsCurrentValue); + } + } + ImGui::InputText("Netkey##SettingsSection_EncKey", (char*)g_szNetKey.c_str(), ImGuiInputTextFlags_ReadOnly); + if (ImGui::Button("Regenerate Encryption Key##SettingsSection_RegenEncKey")) + { + RegenerateEncryptionKey(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: draws the main browser frontend +//----------------------------------------------------------------------------- +void IBrowser::Draw(const char* title, bool* bDraw) +{ + if (!m_bThemeSet) + { + SetStyleVar(); + m_bThemeSet = true; + } + if (!ImGui::Begin(title, bDraw)) + { + ImGui::End(); + return; + } + if (*bDraw == NULL) + { + g_bShowBrowser = false; + } + + ImGui::SetNextWindowSize(ImVec2(840, 600), ImGuiCond_FirstUseEver); + ImGui::SetWindowPos(ImVec2(-500, 50), ImGuiCond_FirstUseEver); + + ImGui::Begin(title, NULL, ImGuiWindowFlags_NoScrollbar); + { + CompMenu(); + + switch (eCurrentSection) + { + case ESection::SERVER_BROWSER: + ServerBrowserSection(); + break; + case ESection::HOST_SERVER: + HostServerSection(); + break; + case ESection::SETTINGS: + SettingsSection(); + break; + default: + break; + } + } + ImGui::End(); +} + +//----------------------------------------------------------------------------- +// Purpose: executes submitted commands in a separate thread +//----------------------------------------------------------------------------- +void IBrowser::ProcessCommand(const char* command_line) +{ + std::thread t(IVEngineClient_CommandExecute, this, command_line); + t.detach(); // Detach from render thread. + + // This is to avoid a race condition. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); +} + +//----------------------------------------------------------------------------- +// Purpose: connects to specified server +//----------------------------------------------------------------------------- +void IBrowser::ConnectToServer(const std::string ip, const std::string port, const std::string encKey) +{ + if (!encKey.empty()) + { + ChangeEncryptionKeyTo(encKey); + } + + std::stringstream cmd; + cmd << "connect " << ip << ":" << port; + ProcessCommand(cmd.str().c_str()); +} + +//----------------------------------------------------------------------------- +// Purpose: connects to specified server +//----------------------------------------------------------------------------- +void IBrowser::ConnectToServer(const std::string connString, const std::string encKey) +{ + if (!encKey.empty()) + { + ChangeEncryptionKeyTo(encKey); + } + + std::stringstream cmd; + cmd << "connect " << connString; + ProcessCommand(cmd.str().c_str()); +} + +//----------------------------------------------------------------------------- +// Purpose: regenerates encryption key +//----------------------------------------------------------------------------- +void IBrowser::RegenerateEncryptionKey() +{ + HNET_GenerateKey(); +} + +//----------------------------------------------------------------------------- +// Purpose: changes encryption key to specified one +//----------------------------------------------------------------------------- +void IBrowser::ChangeEncryptionKeyTo(const std::string str) +{ + HNET_SetKey(str); +} + +//############################################################################# +// ENTRYPOINT +//############################################################################# + +IBrowser* g_pServerBrowser = nullptr; +void DrawBrowser(bool* bDraw) +{ + static IBrowser browser; + static bool AssignPtr = []() + { + g_pServerBrowser = &browser; + return true; + } (); + browser.Draw("Server Browser", bDraw); +} diff --git a/r5dev/gameui/IBrowser.h b/r5dev/gameui/IBrowser.h new file mode 100644 index 00000000..6ff873e3 --- /dev/null +++ b/r5dev/gameui/IBrowser.h @@ -0,0 +1,173 @@ +#pragma once +#ifndef DEDICATED +#include "networksystem/serverlisting.h" +#include "networksystem/r5net.h" + +void DrawBrowser(bool* bDraw); + +class IBrowser +{ +private: + bool m_bThemeSet = false; +public: + IBrowser(); + ~IBrowser(); + + //////////////////// + // Enums // + //////////////////// + enum class ESection + { + SERVER_BROWSER, + HOST_SERVER, + SETTINGS + } eCurrentSection = ESection::SERVER_BROWSER; + + enum class EHostStatus + { + NOT_HOSTING, + HOSTING + } eHostingStatus = EHostStatus::NOT_HOSTING; + + enum class EServerVisibility + { + OFFLINE, + HIDDEN, + PUBLIC + } eServerVisibility = EServerVisibility::OFFLINE; + + //////////////////// + // Server Browser // + //////////////////// +public: + std::vector m_vServerList; + ImGuiTextFilter m_imServerBrowserFilter; + char m_chServerConnStringBuffer[256] = { 0 }; + char m_chServerEncKeyBuffer[30] = { 0 }; + std::string m_szServerListMessage = std::string(); + + std::map mapArray = + { + { "mp_rr_canyonlands_64k_x_64k", "King's Canyon Season 0" }, + { "mp_rr_desertlands_64k_x_64k", "World's Edge Season 3" }, + { "mp_rr_canyonlands_mu1", "King's Canyon Season 2" }, + { "mp_rr_canyonlands_mu1_night", "King's Canyon Season 2 After Dark" }, + { "mp_rr_desertlands_64k_x_64k_nx", "World's Edge Season 3 After Dark" }, + { "mp_lobby", "Lobby Season 3" }, + { "mp_rr_canyonlands_staging", "King's Canyon Firing Range" } + }; + + //////////////////// + // Settings // + //////////////////// + std::string m_szMatchmakingHostName; + + //////////////////// + // Host Server // + //////////////////// + ServerListing m_Server; + std::vector m_vszMapsList; + std::string m_szHostRequestMessage = ""; + std::string m_szHostToken = ""; + ImVec4 m_iv4HostRequestMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + + //////////////////// + // Private Server // + //////////////////// + std::string m_szHiddenServerToken = ""; + std::string m_szHiddenServerRequestMessage = ""; + ImVec4 m_ivHiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); + + /* Texture */ + ID3D11ShaderResourceView* m_idLockedIcon = nullptr; + int m_nLockedIconWidth = 0; + int m_nLockedIconHeight = 0; + std::vector* m_vucLockedIconBlob; + + void SetSection(ESection section) + { + eCurrentSection = section; + } + + //////////////////// + // Style // + //////////////////// + void SetStyleVar() + { + ImGuiStyle& style = ImGui::GetStyle(); + ImVec4* colors = style.Colors; + + colors[ImGuiCol_Text] = ImVec4(0.81f, 0.81f, 0.81f, 1.00f); + colors[ImGuiCol_TextDisabled] = ImVec4(0.56f, 0.56f, 0.56f, 1.00f); + colors[ImGuiCol_WindowBg] = ImVec4(0.27f, 0.27f, 0.27f, 1.00f); + colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_PopupBg] = ImVec4(0.27f, 0.27f, 0.27f, 1.00f); + colors[ImGuiCol_Border] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); + colors[ImGuiCol_BorderShadow] = ImVec4(0.04f, 0.04f, 0.04f, 0.64f); + colors[ImGuiCol_FrameBg] = ImVec4(0.13f, 0.13f, 0.13f, 1.00f); + colors[ImGuiCol_FrameBgHovered] = ImVec4(0.19f, 0.19f, 0.19f, 1.00f); + colors[ImGuiCol_FrameBgActive] = ImVec4(0.24f, 0.24f, 0.24f, 1.00f); + colors[ImGuiCol_TitleBg] = ImVec4(0.22f, 0.22f, 0.22f, 1.00f); + colors[ImGuiCol_TitleBgActive] = ImVec4(0.27f, 0.27f, 0.27f, 1.00f); + colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_MenuBarBg] = ImVec4(0.22f, 0.22f, 0.22f, 1.00f); + colors[ImGuiCol_ScrollbarBg] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f); + colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); + colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); + colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f); + colors[ImGuiCol_CheckMark] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); + colors[ImGuiCol_SliderGrab] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); + colors[ImGuiCol_SliderGrabActive] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); + colors[ImGuiCol_Button] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f); + colors[ImGuiCol_ButtonHovered] = ImVec4(0.45f, 0.45f, 0.45f, 1.00f); + colors[ImGuiCol_ButtonActive] = ImVec4(0.52f, 0.52f, 0.52f, 1.00f); + colors[ImGuiCol_Header] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f); + colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.45f, 1.00f); + colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); + colors[ImGuiCol_Separator] = ImVec4(0.53f, 0.53f, 0.57f, 1.00f); + colors[ImGuiCol_SeparatorHovered] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); + colors[ImGuiCol_SeparatorActive] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f); + colors[ImGuiCol_ResizeGrip] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); + colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.52f, 0.52f, 0.52f, 1.00f); + colors[ImGuiCol_ResizeGripActive] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f); + colors[ImGuiCol_Tab] = ImVec4(0.18f, 0.18f, 0.18f, 1.00f); + colors[ImGuiCol_TabHovered] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); + colors[ImGuiCol_TabActive] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); + + style.WindowBorderSize = 0.0f; + style.FrameBorderSize = 1.0f; + style.ChildBorderSize = 1.0f; + style.PopupBorderSize = 1.0f; + style.TabBorderSize = 1.0f; + + style.WindowRounding = 2.5f; + style.FrameRounding = 0.0f; + style.ChildRounding = 0.0f; + style.PopupRounding = 0.0f; + style.TabRounding = 1.0f; + style.ScrollbarRounding = 1.0f; + + style.ItemSpacing = ImVec2(4, 4); + style.WindowPadding = ImVec2(5, 5); + } + + void RefreshServerList(); + void SendHostingPostRequest(); + void CompMenu(); + void ServerBrowserSection(); + void SettingsSection(); + void HiddenServersModal(); + void HostServerSection(); + void Draw(const char* title, bool* bDraw); + void UpdateHostingStatus(); + void ProcessCommand(const char* command_line); + + void RegenerateEncryptionKey(); + void ChangeEncryptionKeyTo(const std::string str); + + void ConnectToServer(const std::string ip, const std::string port, const std::string encKey); + void ConnectToServer(const std::string connString, const std::string encKey); +}; + +extern IBrowser* g_pServerBrowser; +#endif diff --git a/r5dev/gameui/IConsole.cpp b/r5dev/gameui/IConsole.cpp new file mode 100644 index 00000000..211ade66 --- /dev/null +++ b/r5dev/gameui/IConsole.cpp @@ -0,0 +1,408 @@ +#include "core/stdafx.h" +#include "core/init.h" +#include "tier0/cvar.h" +#include "windows/id3dx.h" +#include "windows/console.h" +#include "gameui/IConsole.h" +#include "client/IVEngineClient.h" + +/****************************************************************************** +------------------------------------------------------------------------------- +File : IConsole.cpp +Date : 18:07:2021 +Author : Kawe Mazidjatari +Purpose: Implements the in-game console frontend +------------------------------------------------------------------------------- +History: +- 15:06:2021 | 14:56 : Created by Kawe Mazidjatari +- 07:08:2021 | 15:22 : Multithread 'CommandExecute' operations to prevent deadlock in render thread +- 07:08:2021 | 15:25 : Fix a race condition that occured when detaching the 'CommandExecute' thread + +******************************************************************************/ + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CConsole::CConsole() +{ + ClearLog(); + memset(m_szInputBuf, 0, sizeof(m_szInputBuf)); + + m_nHistoryPos = -1; + m_bAutoScroll = true; + m_bScrollToBottom = false; + m_bThemeSet = false; + + m_ivCommands.push_back("CLEAR"); + m_ivCommands.push_back("HELP"); + m_ivCommands.push_back("HISTORY"); + AddLog("[DEBUG] THREAD ID: %ld\n", g_dThreadId); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CConsole::~CConsole() +{ + ClearLog(); + for (int i = 0; i < m_ivHistory.Size; i++) + { + free(m_ivHistory[i]); + } +} + +//----------------------------------------------------------------------------- +// Purpose: draws the console frontend +//----------------------------------------------------------------------------- +void CConsole::Draw(const char* title, bool* bDraw) +{ + bool copy_to_clipboard = false; + static auto iConsoleConfig = &g_pImGuiConfig->IConsole_Config; + static auto iBrowserConfig = &g_pImGuiConfig->IBrowser_Config; + + if (!m_bThemeSet) + { + SetStyleVar(); + m_bThemeSet = true; + } + if (iConsoleConfig->m_bAutoClear && Items.Size > iConsoleConfig->m_nAutoClearLimit) // Check if Auto-Clear is enabled and if its above our limit. + { + ClearLog(); + m_ivHistory.clear(); + } + + //ImGui::ShowStyleEditor(); + + ImGui::SetNextWindowSize(ImVec2(1000, 600), ImGuiCond_FirstUseEver); + ImGui::SetWindowPos(ImVec2(-1000, 50), ImGuiCond_FirstUseEver); + + if (!ImGui::Begin(title, bDraw)) + { + ImGui::End(); + return; + } + if (*bDraw == NULL) + { + g_bShowConsole = false; + } + + // Reserve enough left-over height and width for 1 separator + 1 input text + const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); + const float footer_width_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetWindowWidth(); + + /////////////////////////////////////////////////////////////////////// + ImGui::Separator(); + if (ImGui::BeginPopup("Options")) + { + ImGui::Checkbox("Auto-Scroll", &m_bAutoScroll); + + if (ImGui::Checkbox("Auto-Clear", &iConsoleConfig->m_bAutoClear)) + { + g_pImGuiConfig->Save(); + } + + ImGui::SameLine(); + ImGui::PushItemWidth(100); + + if (ImGui::InputInt("Limit##AutoClearAfterCertainIndexCGameConsole", &iConsoleConfig->m_nAutoClearLimit)) + { + g_pImGuiConfig->Save(); + } + + ImGui::PopItemWidth(); + + if (ImGui::SmallButton("Clear")) + { + ClearLog(); + } + + ImGui::SameLine(); + copy_to_clipboard = ImGui::SmallButton("Copy"); + + ImGui::Text("Console Hotkey:"); + ImGui::SameLine(); + + if (ImGui::Hotkey("##OpenIConsoleBind0", &iConsoleConfig->m_nBind0, ImVec2(80, 80))) + { + g_pImGuiConfig->Save(); + } + + ImGui::Text("Browser Hotkey:"); + ImGui::SameLine(); + + if (ImGui::Hotkey("##OpenIBrowserBind0", &iBrowserConfig->m_nBind0, ImVec2(80, 80))) + { + g_pImGuiConfig->Save(); + } + + ImGui::EndPopup(); + } + if (ImGui::Button("Options")) + { + ImGui::OpenPopup("Options"); + } + ImGui::SameLine(); + m_itFilter.Draw("Filter [\"-incl,-excl\"] [\"error\"]", footer_width_to_reserve - 500); + ImGui::Separator(); + + /////////////////////////////////////////////////////////////////////// + ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), true, ImGuiWindowFlags_AlwaysVerticalScrollbar); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 4.f, 6.f }); + if (copy_to_clipboard) + { + ImGui::LogToClipboard(); + } + for (int i = 0; i < Items.Size; i++) + { + const char* item = Items[i]; + if (!m_itFilter.PassFilter(item)) + { + continue; + } + /////////////////////////////////////////////////////////////////// + ImVec4 color; + bool has_color = false; + + /////////////////////////////////////////////////////////////////// + // General + if (strstr(item, "[INFO]")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); has_color = true; } + if (strstr(item, "[ERROR]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, "[DEBUG]")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } + if (strstr(item, "[WARNING]")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } + if (strncmp(item, "# ", 2) == 0) { color = ImVec4(1.00f, 0.80f, 0.60f, 1.00f); has_color = true; } + + /////////////////////////////////////////////////////////////////// + // Script virtual machines per game dll + if (strstr(item, "Script(S):")) { color = ImVec4(0.59f, 0.58f, 0.73f, 1.00f); has_color = true; } + if (strstr(item, "Script(C):")) { color = ImVec4(0.59f, 0.58f, 0.63f, 1.00f); has_color = true; } + if (strstr(item, "Script(U):")) { color = ImVec4(0.59f, 0.48f, 0.53f, 1.00f); has_color = true; } + + /////////////////////////////////////////////////////////////////// + // Native per game dll + if (strstr(item, "Native(S):")) { color = ImVec4(0.59f, 0.58f, 0.73f, 1.00f); has_color = true; } + if (strstr(item, "Native(C):")) { color = ImVec4(0.59f, 0.58f, 0.63f, 1.00f); has_color = true; } + if (strstr(item, "Native(U):")) { color = ImVec4(0.59f, 0.48f, 0.53f, 1.00f); has_color = true; } + + /////////////////////////////////////////////////////////////////// + // Native per sys dll + if (strstr(item, "Native(E):")) { color = ImVec4(0.70f, 0.70f, 0.70f, 1.00f); has_color = true; } + if (strstr(item, "Native(F):")) { color = ImVec4(0.32f, 0.64f, 0.72f, 1.00f); has_color = true; } + if (strstr(item, "Native(R):")) { color = ImVec4(0.36f, 0.70f, 0.35f, 1.00f); has_color = true; } + if (strstr(item, "Native(M):")) { color = ImVec4(0.75f, 0.41f, 0.67f, 1.00f); has_color = true; } + + /////////////////////////////////////////////////////////////////// + // Callbacks + //if (strstr(item, "CodeCallback_")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } + + /////////////////////////////////////////////////////////////////// + // Squirrel VM script errors + if (strstr(item, ".gnut")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.60f); has_color = true; } + if (strstr(item, ".nut")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.60f); has_color = true; } + if (strstr(item, "[CLIENT]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, "[SERVER]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, "[UI]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, "SCRIPT ERROR")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, "SCRIPT COMPILE")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, ".gnut #")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, ".nut #")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + if (strstr(item, "): -> ")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } + + /////////////////////////////////////////////////////////////////// + // Squirrel VM script debug + if (strstr(item, "CALLSTACK")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } + if (strstr(item, "LOCALS")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } + if (strstr(item, "*FUNCTION")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } + if (strstr(item, "DIAGPRINTS")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } + if (strstr(item, " File : ")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } + if (strstr(item, "<><>GRX<><>")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } + + /////////////////////////////////////////////////////////////////// + // Filters + //if (strstr(item, ") -> ")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); has_color = true; } + + if (has_color) { ImGui::PushStyleColor(ImGuiCol_Text, color); } + ImGui::TextWrapped(item); + if (has_color) { ImGui::PopStyleColor(); } + } + if (copy_to_clipboard) { ImGui::LogFinish(); } + + if (m_bScrollToBottom || (m_bAutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) { ImGui::SetScrollHereY(1.0f); } + m_bScrollToBottom = false; + + /////////////////////////////////////////////////////////////////////// + ImGui::PopStyleVar(); + ImGui::EndChild(); + ImGui::Separator(); + + /////////////////////////////////////////////////////////////////////// + // Console + bool reclaim_focus = false; + ImGui::PushItemWidth(footer_width_to_reserve - 80); + if (ImGui::IsWindowAppearing()) { ImGui::SetKeyboardFocusHere(); } + ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory; + + if (ImGui::InputText("##input", m_szInputBuf, IM_ARRAYSIZE(m_szInputBuf), input_text_flags, &TextEditCallbackStub, (void*)this)) + { + char* s = m_szInputBuf; + const char* replace = ""; + if (strstr(m_szInputBuf, "`")) { strcpy_s(s, sizeof(replace), replace); } + Strtrim(s); + if (s[0]) { ProcessCommand(s); } + strcpy_s(s, sizeof(replace), replace); + reclaim_focus = true; + } + + ImGui::SameLine(); + if (ImGui::Button("Submit")) + { + char* s = m_szInputBuf; + const char* replace = ""; + if (s[0]) { ProcessCommand(s); } + strcpy_s(s, sizeof(replace), replace); + reclaim_focus = true; + } + + // Auto-focus on window apparition. + ImGui::SetItemDefaultFocus(); + + // Auto focus previous widget. + if (reclaim_focus) + { + ImGui::SetKeyboardFocusHere(-1); + } + + ImGui::End(); +} + +//----------------------------------------------------------------------------- +// Purpose: executes submitted commands in a separate thread +//----------------------------------------------------------------------------- +void CConsole::ProcessCommand(const char* command_line) +{ + AddLog("# %s\n", command_line); + + std::thread t(IVEngineClient_CommandExecute, this, command_line); + t.detach(); // Detach from render thread. + + // This is to avoid a race condition. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + + m_nHistoryPos = -1; + for (int i = m_ivHistory.Size - 1; i >= 0; i--) + { + if (Stricmp(m_ivHistory[i], command_line) == 0) + { + delete m_ivHistory[i]; + m_ivHistory.erase(m_ivHistory.begin() + i); + break; + } + } + + m_ivHistory.push_back(Strdup(command_line)); + if (Stricmp(command_line, "CLEAR") == 0) + { + ClearLog(); + } + else if (Stricmp(command_line, "HELP") == 0) + { + AddLog("Frontend commands:"); + for (int i = 0; i < m_ivCommands.Size; i++) + { + AddLog("- %s", m_ivCommands[i]); + } + + AddLog("Log types:"); + AddLog("Script(S): = Server (Script VM)"); + AddLog("Script(C): = Client (Script VM)"); + AddLog("Script(U): = UI (Script VM)"); + + AddLog("Native(S): = Server dll (Code)"); + AddLog("Native(C): = Client dll (code)"); + AddLog("Native(U): = UI dll (code)"); + + AddLog("Native(E): = Engine dll (code)"); + AddLog("Native(F): = FileSystem dll (code)"); + AddLog("Native(R): = RTech dll (code)"); + AddLog("Native(M): = MaterialSystem dll (code)"); + } + else if (Stricmp(command_line, "HISTORY") == 0) + { + int first = m_ivHistory.Size - 10; + for (int i = first > 0 ? first : 0; i < m_ivHistory.Size; i++) + { + AddLog("%3d: %s\n", i, m_ivHistory[i]); + } + } + + m_bScrollToBottom = true; +} + +//----------------------------------------------------------------------------- +// Purpose: text edit callback +//----------------------------------------------------------------------------- +int CConsole::TextEditCallback(ImGuiInputTextCallbackData* data) +{ + switch (data->EventFlag) + { + case ImGuiInputTextFlags_CallbackCompletion: + { + // Locate beginning of current word. + const char* word_end = data->Buf + data->CursorPos; + const char* word_start = word_end; + while (word_start > data->Buf) + { + const char c = word_start[-1]; + if (c == ' ' || c == '\t' || c == ',' || c == ';') + { + break; + } + word_start--; + } + break; + } + case ImGuiInputTextFlags_CallbackHistory: + { + const int prev_history_pos = m_nHistoryPos; + if (data->EventKey == ImGuiKey_UpArrow) + { + if (m_nHistoryPos == -1) { m_nHistoryPos = m_ivHistory.Size - 1; } + else if (m_nHistoryPos > 0) { m_nHistoryPos--; } + } + else if (data->EventKey == ImGuiKey_DownArrow) + { + if (m_nHistoryPos != -1) + { + if (++m_nHistoryPos >= m_ivHistory.Size) + { + m_nHistoryPos = -1; + } + } + } + if (prev_history_pos != m_nHistoryPos) + { + const char* history_str = (m_nHistoryPos >= 0) ? m_ivHistory[m_nHistoryPos] : ""; + data->DeleteChars(0, data->BufTextLen); + data->InsertChars(0, history_str); + } + } + } + return 0; +} + +//############################################################################# +// ENTRYPOINT +//############################################################################# + +CConsole* g_GameConsole = nullptr; +void DrawConsole(bool* bDraw) +{ + static CConsole console; + static bool AssignPtr = []() + { + g_GameConsole = &console; + return true; + } (); + console.Draw("Console", bDraw); +} + +/////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/include/CGameConsole.h b/r5dev/gameui/IConsole.h similarity index 84% rename from r5dev/include/CGameConsole.h rename to r5dev/gameui/IConsole.h index 764f6839..3d9e9b96 100644 --- a/r5dev/include/CGameConsole.h +++ b/r5dev/gameui/IConsole.h @@ -1,40 +1,42 @@ #pragma once -#include "gui_utility.h" +#ifndef DEDICATED + +/////////////////////////////////////////////////////////////////////////////// +// Initialization +void DrawConsole(bool* bDraw); /////////////////////////////////////////////////////////////////////////////// // Globals -extern ImVector Items; +inline ImVector Items; -class CGameConsole +class CConsole { private: /////////////////////////////////////////////////////////////////////////// - char InputBuf[256] = { 0 }; - ImVector Commands; - ImVector History; - int HistoryPos = -1; - ImGuiTextFilter Filter; - bool AutoScroll = true; - bool ScrollToBottom = false; - bool ThemeSet = false; + char m_szInputBuf[256] = { 0 }; + ImVector m_ivCommands; + ImVector m_ivHistory; + int m_nHistoryPos = -1; + ImGuiTextFilter m_itFilter; + bool m_bAutoScroll = true; + bool m_bScrollToBottom = false; + bool m_bThemeSet = false; public: /////////////////////////////////////////////////////////////////////////// - CGameConsole(); - ~CGameConsole(); + CConsole(); + ~CConsole(); - void Draw(const char* title); + void Draw(const char* title, bool* bDraw); void ProcessCommand(const char* command_line); - void ExecCommand(const char* command_line); int TextEditCallback(ImGuiInputTextCallbackData* data); - bool ShouldPrintToCommandPrompt() { return g_GuiConfig.CGameConsoleConfig.printCmd; }; /////////////////////////////////////////////////////////////////////////// // History static int TextEditCallbackStub(ImGuiInputTextCallbackData* data) { - CGameConsole* console = (CGameConsole*)data->UserData; + CConsole* console = (CConsole*)data->UserData; return console->TextEditCallback(data); } @@ -42,9 +44,9 @@ public: // Utility void ClearLog() { + for (int i = 0; i < Items.Size; i++) { free(Items[i]); } Items.clear(); } - void AddLog(const char* fmt, ...) IM_FMTARGS(2) { char buf[1024]; @@ -118,4 +120,5 @@ public: } }; -extern CGameConsole* g_GameConsole; \ No newline at end of file +extern CConsole* g_GameConsole; +#endif // !DEDICATED diff --git a/r5dev/include/CCompanion.h b/r5dev/include/CCompanion.h deleted file mode 100644 index 93713b1a..00000000 --- a/r5dev/include/CCompanion.h +++ /dev/null @@ -1,163 +0,0 @@ -#pragma once -#include "gui_utility.h" -#include "r5net.h" - -class CCompanion -{ -private: - bool ThemeSet = false; -public: - CCompanion(); - ~CCompanion(); - - //////////////////// - // Enums // - //////////////////// - enum class ESection { - ServerBrowser, - HostServer, - Settings - } CurrentSection = ESection::ServerBrowser; - - enum class EHostStatus { - NotHosting, - Hosting - } HostingStatus = EHostStatus::NotHosting; - - enum class EServerVisibility { - Offline, - Hidden, - Public - } ServerVisibility = EServerVisibility::Offline; - - //////////////////// - // Server Browser // - //////////////////// -private: - R5Net::Client* r5net = nullptr; -public: - R5Net::Client* GetR5Net() { return r5net; } - - std::vector ServerList; - ImGuiTextFilter ServerBrowserFilter; - char ServerConnStringBuffer[256] = { 0 }; - char ServerEncKeyBuffer[30] = { 0 }; - std::string ServerListMessage = std::string(); - - //////////////////// - // Settings // - //////////////////// - std::string MatchmakingServerStringBuffer; - - //////////////////// - // Host Server // - //////////////////// - ServerListing MyServer; - std::vector MapsList; - std::string HostRequestMessage = ""; - std::string HostToken = ""; - ImVec4 HostRequestMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - bool StartAsDedi = false; - bool OverridePlaylist = false; - - //////////////////// - // Private Server // - //////////////////// - std::string HiddenServerToken = ""; - std::string HiddenServerRequestMessage = ""; - ImVec4 HiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); - - /* Texture */ - ID3D11ShaderResourceView* ApexLockIcon = nullptr; - int ApexLockIconWidth = 48; - int ApexLockIconHeight = 48; - - void SetSection(ESection section) - { - CurrentSection = section; - } - - //////////////////// - // Style // - //////////////////// - void SetStyleVar() - { - ImGuiStyle& style = ImGui::GetStyle(); - ImVec4* colors = style.Colors; - - colors[ImGuiCol_Text] = ImVec4(0.81f, 0.81f, 0.81f, 1.00f); - colors[ImGuiCol_TextDisabled] = ImVec4(0.56f, 0.56f, 0.56f, 1.00f); - colors[ImGuiCol_WindowBg] = ImVec4(0.27f, 0.27f, 0.27f, 1.00f); - colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_PopupBg] = ImVec4(0.27f, 0.27f, 0.27f, 1.00f); - colors[ImGuiCol_Border] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); - colors[ImGuiCol_BorderShadow] = ImVec4(0.04f, 0.04f, 0.04f, 0.64f); - colors[ImGuiCol_FrameBg] = ImVec4(0.13f, 0.13f, 0.13f, 1.00f); - colors[ImGuiCol_FrameBgHovered] = ImVec4(0.19f, 0.19f, 0.19f, 1.00f); - colors[ImGuiCol_FrameBgActive] = ImVec4(0.24f, 0.24f, 0.24f, 1.00f); - colors[ImGuiCol_TitleBg] = ImVec4(0.22f, 0.22f, 0.22f, 1.00f); - colors[ImGuiCol_TitleBgActive] = ImVec4(0.27f, 0.27f, 0.27f, 1.00f); - colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_MenuBarBg] = ImVec4(0.22f, 0.22f, 0.22f, 1.00f); - colors[ImGuiCol_ScrollbarBg] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f); - colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); - colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); - colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f); - colors[ImGuiCol_CheckMark] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); - colors[ImGuiCol_SliderGrab] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); - colors[ImGuiCol_SliderGrabActive] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); - colors[ImGuiCol_Button] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f); - colors[ImGuiCol_ButtonHovered] = ImVec4(0.45f, 0.45f, 0.45f, 1.00f); - colors[ImGuiCol_ButtonActive] = ImVec4(0.52f, 0.52f, 0.52f, 1.00f); - colors[ImGuiCol_Header] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f); - colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.45f, 1.00f); - colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); - colors[ImGuiCol_Separator] = ImVec4(0.53f, 0.53f, 0.57f, 1.00f); - colors[ImGuiCol_SeparatorHovered] = ImVec4(0.53f, 0.53f, 0.53f, 1.00f); - colors[ImGuiCol_SeparatorActive] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f); - colors[ImGuiCol_ResizeGrip] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); - colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.52f, 0.52f, 0.52f, 1.00f); - colors[ImGuiCol_ResizeGripActive] = ImVec4(0.63f, 0.63f, 0.63f, 1.00f); - colors[ImGuiCol_Tab] = ImVec4(0.18f, 0.18f, 0.18f, 1.00f); - colors[ImGuiCol_TabHovered] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); - colors[ImGuiCol_TabActive] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); - - style.WindowBorderSize = 0.0f; - style.FrameBorderSize = 1.0f; - style.ChildBorderSize = 1.0f; - style.PopupBorderSize = 1.0f; - style.TabBorderSize = 1.0f; - - style.WindowRounding = 2.5f; - style.FrameRounding = 0.0f; - style.ChildRounding = 0.0f; - style.PopupRounding = 0.0f; - style.TabRounding = 1.0f; - style.ScrollbarRounding = 1.0f; - - style.ItemSpacing = ImVec2(4, 4); - style.WindowPadding = ImVec2(5, 5); - } - - void RefreshServerList(); - void SendHostingPostRequest(); - void CompMenu(); - void ServerBrowserSection(); - void SettingsSection(); - void HiddenServersModal(); - void HostServerSection(); - void Draw(const char* title); - void UpdateHostingStatus(); - void ProcessCommand(const char* command_line); - void ExecCommand(const char* command_line); - - void RegenerateEncryptionKey(); - void ChangeEncryptionKeyTo(const std::string str); - - void ConnectToServer(const std::string ip, const std::string port, const std::string encKey); - void ConnectToServer(const std::string connString, const std::string encKey); -}; - -extern CCompanion* g_ServerBrowser; - -extern bool g_CheckCompBanDB; \ No newline at end of file diff --git a/r5dev/include/console.h b/r5dev/include/console.h deleted file mode 100644 index 6e5f6aed..00000000 --- a/r5dev/include/console.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -///////////////////////////////////////////////////////////////////////////// -// Initialization -void SetupConsole(); \ No newline at end of file diff --git a/r5dev/include/enums.h b/r5dev/include/enums.h deleted file mode 100644 index afe89dda..00000000 --- a/r5dev/include/enums.h +++ /dev/null @@ -1,790 +0,0 @@ -#pragma once - -/* Enumerations */ -enum class D3D11DeviceVTbl : short -{ - // IUnknown - QueryInterface = 0, - AddRef = 1, - Release = 2, - - // ID3D11Device - CreateBuffer = 3, - CreateTexture1D = 4, - CreateTexture2D = 5, - CreateTexture3D = 6, - CreateShaderResourceView = 7, - CreateUnorderedAccessView = 8, - CreateRenderTargetView = 9, - CreateDepthStencilView = 10, - CreateInputLayout = 11, - CreateVertexShader = 12, - CreateGeometryShader = 13, - CreateGeometryShaderWithStreamOutput = 14, - CreatePixelShader = 15, - CreateHullShader = 16, - CreateDomainShader = 17, - CreateComputeShader = 18, - CreateClassLinkage = 19, - CreateBlendState = 20, - CreateDepthStencilState = 21, - CreateRasterizerState = 22, - CreateSamplerState = 23, - CreateQuery = 24, - CreatePredicate = 25, - CreateCounter = 26, - CreateDeferredContext = 27, - OpenSharedResource = 28, - CheckFormatSupport = 29, - CheckMultisampleQualityLevels = 30, - CheckCounterInfo = 31, - CheckCounter = 32, - CheckFeatureSupport = 33, - GetPrivateData = 34, - SetPrivateData = 35, - SetPrivateDataInterface = 36, - GetFeatureLevel = 37, - GetCreationFlags = 38, - GetDeviceRemovedReason = 39, - GetImmediateContext = 40, - SetExceptionMode = 41, - GetExceptionMode = 42, -}; - -enum class DXGISwapChainVTbl : short -{ - // IUnknown - QueryInterface = 0, - AddRef = 1, - Release = 2, - - // IDXGIObject - SetPrivateData = 3, - SetPrivateDataInterface = 4, - GetPrivateData = 5, - GetParent = 6, - - // IDXGIDeviceSubObject - GetDevice = 7, - - // IDXGISwapChain - Present = 8, - GetBuffer = 9, - SetFullscreenState = 10, - GetFullscreenState = 11, - GetDesc = 12, - ResizeBuffers = 13, - ResizeTarget = 14, - GetContainingOutput = 15, - GetFrameStatistics = 16, - GetLastPresentCount = 17, -}; - -#define MAX_SPLITSCREEN_CLIENT_BITS 2 -#define MAX_SPLITSCREEN_CLIENTS ( 1 << MAX_SPLITSCREEN_CLIENT_BITS ) // 4 - -enum -{ - MAX_JOYSTICKS = MAX_SPLITSCREEN_CLIENTS, - MOUSE_BUTTON_COUNT = 5, -}; - -enum JoystickAxis_t -{ - JOY_AXIS_X = 0, - JOY_AXIS_Y, - JOY_AXIS_Z, - JOY_AXIS_R, - JOY_AXIS_U, - JOY_AXIS_V, - MAX_JOYSTICK_AXES, -}; - -enum -{ - JOYSTICK_MAX_BUTTON_COUNT = 32, - JOYSTICK_POV_BUTTON_COUNT = 4, - JOYSTICK_AXIS_BUTTON_COUNT = MAX_JOYSTICK_AXES * 2, -}; - -#define JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_BUTTON + ((_joystick) * JOYSTICK_MAX_BUTTON_COUNT) + (_button) ) -#define JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_POV_BUTTON + ((_joystick) * JOYSTICK_POV_BUTTON_COUNT) + (_button) ) -#define JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_AXIS_BUTTON + ((_joystick) * JOYSTICK_AXIS_BUTTON_COUNT) + (_button) ) - -#define JOYSTICK_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ) -#define JOYSTICK_POV_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ) -#define JOYSTICK_AXIS_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ) - -enum ButtonCode_t -{ - BUTTON_CODE_INVALID = -1, - BUTTON_CODE_NONE = 0, - - KEY_FIRST = 0, - - KEY_NONE = KEY_FIRST, - KEY_0, - KEY_1, - KEY_2, - KEY_3, - KEY_4, - KEY_5, - KEY_6, - KEY_7, - KEY_8, - KEY_9, - KEY_A, - KEY_B, - KEY_C, - KEY_D, - KEY_E, - KEY_F, - KEY_G, - KEY_H, - KEY_I, - KEY_J, - KEY_K, - KEY_L, - KEY_M, - KEY_N, - KEY_O, - KEY_P, - KEY_Q, - KEY_R, - KEY_S, - KEY_T, - KEY_U, - KEY_V, - KEY_W, - KEY_X, - KEY_Y, - KEY_Z, - KEY_PAD_0, - KEY_PAD_1, - KEY_PAD_2, - KEY_PAD_3, - KEY_PAD_4, - KEY_PAD_5, - KEY_PAD_6, - KEY_PAD_7, - KEY_PAD_8, - KEY_PAD_9, - KEY_PAD_DIVIDE, - KEY_PAD_MULTIPLY, - KEY_PAD_MINUS, - KEY_PAD_PLUS, - KEY_PAD_ENTER, - KEY_PAD_DECIMAL, - KEY_LBRACKET, - KEY_RBRACKET, - KEY_SEMICOLON, - KEY_APOSTROPHE, - KEY_BACKQUOTE, - KEY_COMMA, - KEY_PERIOD, - KEY_SLASH, - KEY_BACKSLASH, - KEY_MINUS, - KEY_EQUAL, - KEY_ENTER, - KEY_SPACE, - KEY_BACKSPACE, - KEY_TAB, - KEY_CAPSLOCK, - KEY_NUMLOCK, - KEY_ESCAPE, - KEY_SCROLLLOCK, - KEY_INSERT, - KEY_DELETE, - KEY_HOME, - KEY_END, - KEY_PAGEUP, - KEY_PAGEDOWN, - KEY_BREAK, - KEY_LSHIFT, - KEY_RSHIFT, - KEY_LALT, - KEY_RALT, - KEY_LCONTROL, - KEY_RCONTROL, - KEY_LWIN, - KEY_RWIN, - KEY_APP, - KEY_UP, - KEY_LEFT, - KEY_DOWN, - KEY_RIGHT, - KEY_F1, - KEY_F2, - KEY_F3, - KEY_F4, - KEY_F5, - KEY_F6, - KEY_F7, - KEY_F8, - KEY_F9, - KEY_F10, - KEY_F11, - KEY_F12, - KEY_CAPSLOCKTOGGLE, - KEY_NUMLOCKTOGGLE, - KEY_SCROLLLOCKTOGGLE, - - KEY_LAST = KEY_SCROLLLOCKTOGGLE, - KEY_COUNT = KEY_LAST - KEY_FIRST + 1, - - // Mouse - MOUSE_FIRST = KEY_LAST + 1, - - MOUSE_LEFT = MOUSE_FIRST, - MOUSE_RIGHT, - MOUSE_MIDDLE, - MOUSE_4, - MOUSE_5, - MOUSE_WHEEL_UP, // A fake button which is 'pressed' and 'released' when the wheel is moved up - MOUSE_WHEEL_DOWN, // A fake button which is 'pressed' and 'released' when the wheel is moved down - - MOUSE_LAST = MOUSE_WHEEL_DOWN, - MOUSE_COUNT = MOUSE_LAST - MOUSE_FIRST + 1, - - // Joystick - JOYSTICK_FIRST = MOUSE_LAST + 1, - - JOYSTICK_FIRST_BUTTON = JOYSTICK_FIRST, - JOYSTICK_LAST_BUTTON = JOYSTICK_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_MAX_BUTTON_COUNT - 1), - JOYSTICK_FIRST_POV_BUTTON, - JOYSTICK_LAST_POV_BUTTON = JOYSTICK_POV_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_POV_BUTTON_COUNT - 1), - JOYSTICK_FIRST_AXIS_BUTTON, - JOYSTICK_LAST_AXIS_BUTTON = JOYSTICK_AXIS_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_AXIS_BUTTON_COUNT - 1), - - JOYSTICK_LAST = JOYSTICK_LAST_AXIS_BUTTON, - - BUTTON_CODE_LAST, - BUTTON_CODE_COUNT = BUTTON_CODE_LAST - KEY_FIRST + 1, - - // Helpers for XBox 360 - KEY_XBUTTON_UP = JOYSTICK_FIRST_POV_BUTTON, // POV buttons - KEY_XBUTTON_RIGHT, - KEY_XBUTTON_DOWN, - KEY_XBUTTON_LEFT, - - KEY_XBUTTON_A = JOYSTICK_FIRST_BUTTON, // Buttons - KEY_XBUTTON_B, - KEY_XBUTTON_X, - KEY_XBUTTON_Y, - KEY_XBUTTON_LEFT_SHOULDER, - KEY_XBUTTON_RIGHT_SHOULDER, - KEY_XBUTTON_BACK, - KEY_XBUTTON_START, - KEY_XBUTTON_STICK1, - KEY_XBUTTON_STICK2, - KEY_XBUTTON_INACTIVE_START, - - KEY_XSTICK1_RIGHT = JOYSTICK_FIRST_AXIS_BUTTON, // XAXIS POSITIVE - KEY_XSTICK1_LEFT, // XAXIS NEGATIVE - KEY_XSTICK1_DOWN, // YAXIS POSITIVE - KEY_XSTICK1_UP, // YAXIS NEGATIVE - KEY_XBUTTON_LTRIGGER, // ZAXIS POSITIVE - KEY_XBUTTON_RTRIGGER, // ZAXIS NEGATIVE - KEY_XSTICK2_RIGHT, // UAXIS POSITIVE - KEY_XSTICK2_LEFT, // UAXIS NEGATIVE - KEY_XSTICK2_DOWN, // VAXIS POSITIVE - KEY_XSTICK2_UP, // VAXIS NEGATIVE -}; - -// Buttons are not confirmed to be the same. They have been always the same throughout the source engine. Lets hope they did not change them. - -enum KeyValuesTypes -{ - TYPE_NONE = 0x0, - TYPE_STRING = 0x1, - TYPE_INT = 0x2, - TYPE_FLOAT = 0x3, - TYPE_PTR = 0x4, - TYPE_WSTRING = 0x5, - TYPE_COLOR = 0x6, - TYPE_UINT64 = 0x7, - TYPE_COMPILED_INT_BYTE = 0x8, - TYPE_COMPILED_INT_0 = 0x9, - TYPE_COMPILED_INT_1 = 0xA, - TYPE_NUMTYPES = 0xB, -}; - -enum ClientFrameStage_t -{ - FRAME_UNDEFINED = -1, // (haven't run any frames yet) - FRAME_START, - - // A network packet is being recieved - FRAME_NET_UPDATE_START, - // Data has been received and we're going to start calling PostDataUpdate - FRAME_NET_UPDATE_POSTDATAUPDATE_START, - // Data has been received and we've called PostDataUpdate on all data recipients - FRAME_NET_UPDATE_POSTDATAUPDATE_END, - // We've received all packets, we can now do interpolation, prediction, etc.. - FRAME_NET_UPDATE_END, - - // We're about to start rendering the scene - FRAME_RENDER_START, - // We've finished rendering the scene. - FRAME_RENDER_END, - - FRAME_NET_FULL_FRAME_UPDATE_ON_REMOVE -}; - -enum class HostStates_t : int -{ - HS_NEW_GAME = 0x0, - HS_LOAD_GAME = 0x1, - HS_CHANGE_LEVEL_SP = 0x2, - HS_CHANGE_LEVEL_MP = 0x3, - HS_RUN = 0x4, - HS_GAME_SHUTDOWN = 0x5, - HS_SHUTDOWN = 0x6, - HS_RESTART = 0x7, -}; - -enum SIGNONSTATE -{ - SIGNONSTATE_NONE = 0, // no state yet; about to connect - SIGNONSTATE_CHALLENGE = 1, // client challenging server; all OOB packets - SIGNONSTATE_CONNECTED = 2, // client is connected to server; netchans ready - SIGNONSTATE_NEW = 3, // just got serverinfo and string tables - SIGNONSTATE_PRESPAWN = 4, // received signon buffers - SIGNONSTATE_GETTING_DATA = 5, // getting persistence data I assume? - SIGNONSTATE_SPAWN = 6, // ready to receive entity packets - SIGNONSTATE_FIRST_SNAP = 7, // ??? - SIGNONSTATE_FULL = 8, // we are fully connected; first non-delta packet received - SIGNONSTATE_CHANGELEVEL = 9, // server is changing level; please wait -}; - -enum FileWarningLevel_t -{ - FILESYSTEM_WARNING = -1, - FILESYSTEM_WARNING_QUIET = 0, - FILESYSTEM_WARNING_REPORTUNCLOSED, - FILESYSTEM_WARNING_REPORTUSAGE, - FILESYSTEM_WARNING_REPORTALLACCESSES, - FILESYSTEM_WARNING_REPORTALLACCESSES_READ, - FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE, - FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC -}; - -#define FCVAR_NONE 0 - -// Command to ConVars and ConCommands -// ConVar Systems -#define FCVAR_UNREGISTERED (1<<0) // If this is set, don't add to linked list, etc. -#define FCVAR_DEVELOPMENTONLY (1<<1) // Hidden in released products. Flag is removed automatically if ALLOW_DEVELOPMENT_CVARS is defined. -#define FCVAR_GAMEDLL (1<<2) // defined by the game DLL -#define FCVAR_CLIENTDLL (1<<3) // defined by the client DLL -#define FCVAR_HIDDEN (1<<4) // Hidden. Doesn't appear in find or auto complete. Like DEVELOPMENTONLY, but can't be compiled out. - -// ConVar only -#define FCVAR_PROTECTED (1<<5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value -#define FCVAR_SPONLY (1<<6) // This cvar cannot be changed by clients connected to a multiplayer server. -#define FCVAR_ARCHIVE (1<<7) // set to cause it to be saved to vars.rc -#define FCVAR_NOTIFY (1<<8) // notifies players when changed -#define FCVAR_USERINFO (1<<9) // changes the client's info string - -#define FCVAR_PRINTABLEONLY (1<<10) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). - -#define FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS (1<<10) // When on concommands this allows remote clients to execute this cmd on the server. - // We are changing the default behavior of concommands to disallow execution by remote clients without - // this flag due to the number existing concommands that can lag or crash the server when clients abuse them. - -#define FCVAR_UNLOGGED (1<<11) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log -#define FCVAR_NEVER_AS_STRING (1<<12) // never try to print that cvar - -// It's a ConVar that's shared between the client and the server. -// At signon, the values of all such ConVars are sent from the server to the client (skipped for local -// client, of course ) -// If a change is requested it must come from the console (i.e., no remote client changes) -// If a value is changed while a server is active, it's replicated to all connected clients -#define FCVAR_REPLICATED (1<<13) // server setting enforced on clients, TODO rename to FCAR_SERVER at some time -#define FCVAR_CHEAT (1<<14) // Only useable in singleplayer / debug / multiplayer & sv_cheats -#define FCVAR_SS (1<<15) // causes varnameN where N == 2 through max splitscreen slots for mod to be autogenerated -#define FCVAR_DEMO (1<<16) // record this cvar when starting a demo file -#define FCVAR_DONTRECORD (1<<17) // don't record these command in demofiles -#define FCVAR_SS_ADDED (1<<18) // This is one of the "added" FCVAR_SS variables for the splitscreen players -#define FCVAR_RELEASE (1<<19) // Cvars tagged with this are the only cvars avaliable to customers -#define FCVAR_RELOAD_MATERIALS (1<<20) // If this cvar changes, it forces a material reload -#define FCVAR_RELOAD_TEXTURES (1<<21) // If this cvar changes, if forces a texture reload - -#define FCVAR_NOT_CONNECTED (1<<22) // cvar cannot be changed by a client that is connected to a server -#define FCVAR_MATERIAL_SYSTEM_THREAD (1<<23) // Indicates this cvar is read from the material system thread -#define FCVAR_ARCHIVE_GAMECONSOLE (1<<24) // cvar written to config.cfg on the Xbox - -#define FCVAR_SERVER_CAN_EXECUTE (1<<28)// the server is allowed to execute this command on clients via ClientCommand/NET_StringCmd/CBaseClientState::ProcessStringCmd. -#define FCVAR_SERVER_CANNOT_QUERY (1<<29)// If this is set, then the server is not allowed to query this cvar's value (via IServerPluginHelpers::StartQueryCvarValue). -#define FCVAR_CLIENTCMD_CAN_EXECUTE (1<<30) // IVEngineClient::ClientCmd is allowed to execute this command. - -#define MAX_PLAYERS 128 // Max R5 players. - -/// \ingroup error -#define ORIGIN_ERROR 0xA0000000 ///< Add this to your error code to get an error. Bit 29 is set to distinguish Origin error codes from system error codes. - -/// \ingroup error -#define ORIGIN_WARNING 0x40000000 ///< Add this to your warning code to get a warning. Bit 29 is set to distinguish Origin warning codes from system warnings codes. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_GENERAL 0x00000000 ///< Add this to your error to get a general error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_SDK (1<<16) ///< Add this to your error to get an SDK error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_CORE (2<<16) ///< Add this to your error to get a core error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_IGO (3<<16) ///< Add this to your error to get an IGO error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_FRIENDS (4<<16) ///< Add this to your error to get a friends error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_PRESENCE (5<<16) ///< Add this to your error to get a presence error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_COMMERCE (6<<16) ///< Add this to your error to get a commerce error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_ACHIEVEMENTS (7<<16) ///< Add this to your error to get a achievement error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_LSX (8<<16) ///< Add this to your error to get an LSX error. - -/// \ingroup error -#define ORIGIN_ERROR_AREA_PROXY (9<<16) ///< Add this to your error to get an Origin Proxy error. - - -/// \ingroup error -#define ORIGIN_ERROR_LEVEL_SHIFT 24 ///< [A description is required for this define] - -/// \ingroup error -#define ORIGIN_ERROR_LEVEL_MASK 0x0F000000 ///< The error level mask. - -/// \ingroup error -#define ORIGIN_LEVEL_0 (0<<24) ///< A severe error. - -/// \ingroup error -#define ORIGIN_LEVEL_1 (1<<24) ///< A major error. - -/// \ingroup error -#define ORIGIN_LEVEL_2 (2<<24) ///< A minor error. - -/// \ingroup error -#define ORIGIN_LEVEL_3 (3<<24) ///< A trivial error. - -/// \ingroup error -#define ORIGIN_LEVEL_4 (4<<24) ///< Every error. - -/// @} - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - -/// $errors - -#endif /* DOXYGEN_SHOULD_SKIP_THIS */ - - - -/// \name Origin Error Codes -/// These defines specify Origin-specific errors. -/// @{ - -/// \ingroup error -#define ORIGIN_SUCCESS 0 ///< The operation succeeded. - -/// \ingroup error -#define ORIGIN_PENDING 1 ///< The operation is still waiting to complete. - -// General error codes - -/// \ingroup error -#define ORIGIN_ERROR_GENERAL -1 ///< An unspecified error has occured. - -/// \ingroup error -#define ORIGIN_ERROR_INVALID_HANDLE (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_GENERAL + 0) ///< The provided handle is invalid. - -/// \ingroup error -#define ORIGIN_ERROR_OUT_OF_MEMORY (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_GENERAL + 1) ///< Failed to allocate memory. - -/// \ingroup error -#define ORIGIN_ERROR_NOT_IMPLEMENTED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_GENERAL + 2) ///< The function is not implemented. - -/// \ingroup error -#define ORIGIN_ERROR_INVALID_USER (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 3) ///< The specified user is not valid in this context, or the userId is invalid. - -/// \ingroup error -#define ORIGIN_ERROR_INVALID_ARGUMENT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 4) ///< One or more arguments are invalid. - -/// \ingroup error -#define ORIGIN_ERROR_NO_CALLBACK_SPECIFIED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 5) ///< The asynchronous operation expected a callback, but no callback was specified. - -/// \ingroup error -#define ORIGIN_ERROR_BUFFER_TOO_SMALL (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 6) ///< The provided buffer doesn't have enough space to contain the requested data. - -/// \ingroup error -#define ORIGIN_ERROR_TOO_MANY_VALUES_IN_LIST (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 7) ///< We are currently only supporting one item in the list. - -/// \ingroup error -#define ORIGIN_ERROR_NOT_FOUND (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 8) ///< The requested item was not found. - -/// \ingroup error -#define ORIGIN_ERROR_INVALID_PERSONA (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 9) ///< The specified persona is not valid in this context, or the personaId is invalid. - -/// \ingroup error -#define ORIGIN_ERROR_NO_NETWORK (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 10) ///< No internet connection available. - -/// \ingroup error -#define ORIGIN_ERROR_NO_SERVICE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 11) ///< Origin services are unavailable. - -/// \ingroup error -#define ORIGIN_ERROR_NOT_LOGGED_IN (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 12) ///< The user isn't logged in. No valid session is available. - -/// \ingroup error -#define ORIGIN_ERROR_MANDATORY_ORIGIN_UPDATE_PENDING (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 13) ///< There is a mandatory update pending for Origin, this will prevent origin from going online. - -/// \ingroup error -#define ORIGIN_ERROR_ACCOUNT_IN_USE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 14) ///< The account is currently in use by another Origin instance. - -/// \ingroup error -#define ORIGIN_ERROR_TOO_MANY_INSTANCES (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_GENERAL + 15) ///< Too many instances of the OriginSDK created. - -/// \ingroup error -#define ORIGIN_ERROR_ALREADY_EXISTS (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_GENERAL + 16) ///< The item already exists in the list. - -/// \ingroup error -#define ORIGIN_ERROR_INVALID_OPERATION (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 17) ///< The requested operation cannot be performed. - -/// \ingroup error -#define ORIGIN_ERROR_AGE_RESTRICTED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 18) ///< The item has age restrictions. - -/// \ingroup error -#define ORIGIN_ERROR_BANNED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 19) ///< The user is banned. - -/// \ingroup error -#define ORIGIN_ERROR_NOT_READY (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_GENERAL + 20) ///< The item is not ready. - -/// @} - -/// \name SDK Error Codes -/// These defines specify Origin SDK-specific errors. -/// @{ - -// Sdk error codes - -/// \ingroup error -#define ORIGIN_ERROR_SDK_NOT_INITIALIZED (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_SDK + 0) ///< The Origin SDK was not running. - -/// \ingroup error -#define ORIGIN_ERROR_SDK_INVALID_ALLOCATOR_DEALLOCATOR_COMBINATION (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_SDK + 1) ///< Make sure that you provide both an allocator and a deallocator. - -/// \ingroup error -#define ORIGIN_ERROR_SDK_IS_RUNNING (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 2) ///< The Origin SDK is running. This operation should only be done before the SDK is initialized or after the SDK is shutdown. - -/// \ingroup error -#define ORIGIN_ERROR_SDK_NOT_ALL_RESOURCES_RELEASED (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_SDK + 3) ///< The game is still holding on to the resource handles. Call #OriginDestroyHandle on resources that are no longer needed. - -/// \ingroup error -#define ORIGIN_ERROR_SDK_INVALID_RESOURCE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 4) ///< The resource in the resource map is invalid. Memory corruption? - -/// \ingroup error -#define ORIGIN_ERROR_SDK_INTERNAL_ERROR (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_SDK + 5) ///< The SDK experienced an internal error. - -/// \ingroup error -#define ORIGIN_ERROR_SDK_INTERNAL_BUFFER_TOO_SMALL (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_SDK + 6) ///< The internal buffer that the SDK is using is not big enough to receive the response. Inform OriginSDK Support. - -/// @} - -/// \name SDK Warning Codes -/// These defines specify Origin SDK-specific warnings. -/// @{ - -/// \ingroup error -#define ORIGIN_WARNING_SDK_ALREADY_INITIALIZED (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 1) ///< The Origin SDK is already initialized. - -/// \ingroup error -#define ORIGIN_WARNING_SDK_STILL_RUNNING (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 2) ///< The Origin SDK is still running. - -/// \ingroup error -#define ORIGIN_WARNING_SDK_ENUMERATOR_IN_USE (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 3) ///< The Enumerator associated with the handle was in use. - -/// \ingroup error -#define ORIGIN_WARNING_SDK_ENUMERATOR_TERMINATED (ORIGIN_WARNING + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_SDK + 4) ///< The Enumerator associated with the handle was not finished. - -/// @} - -/// \name Core Error Codes -/// These defines specify Origin Core-specific errors. -/// @{ - -/// \ingroup error -#define ORIGIN_ERROR_CORE_NOTLOADED (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_CORE + 0) ///< The Origin desktop application is not loaded. - -/// \ingroup error -#define ORIGIN_ERROR_CORE_LOGIN_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 1) ///< Origin couldn't authenticate with the Origin Servers. - -/// \ingroup error -#define ORIGIN_ERROR_CORE_AUTHENTICATION_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 2) ///< Origin seems to be running, but the LSX Authentication Challenge failed. No communication with Core is possible. - -/// \ingroup error -#define ORIGIN_ERROR_CORE_SEND_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 4) ///< Sending data to Origin failed. - -/// \ingroup error -#define ORIGIN_ERROR_CORE_RECEIVE_FAILED (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 5) ///< Receiving data from Origin failed. - -/// \ingroup error -#define ORIGIN_ERROR_CORE_RESOURCE_NOT_FOUND (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_CORE + 6) ///< The requested resource could not be located. - -/// \ingroup error -#define ORIGIN_ERROR_CORE_INCOMPATIBLE_VERSION (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_CORE + 7) ///< The Origin version is too old to work with this SDK version. - -/// \ingroup error -#define ORIGIN_ERROR_CORE_NOT_INSTALLED (ORIGIN_ERROR + ORIGIN_LEVEL_0 + ORIGIN_ERROR_AREA_CORE + 8) ///< The Origin installation couldn't be found. - -/// @} - -/// \name IGO Error and Warning Codes -/// These defines specify In-game Overlay errors and warnings. -/// @{ - -/// \ingroup error -#define ORIGIN_WARNING_IGO_NOTLOADED (ORIGIN_WARNING + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_IGO + 0) ///< The IGO could not be loaded, so SDK functionality is degraded. - -/// \ingroup error -#define ORIGIN_WARNING_IGO_SUPPORT_NOTLOADED (ORIGIN_WARNING + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_IGO + 1) ///< IGO support is not loaded, so SDK functionality is degraded. - -/// \ingroup error -#define ORIGIN_ERROR_IGO_ILLEGAL_ANCHOR_POINT (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_IGO + 2) ///< The combination of anchor point bits doesn't resolve to a proper dialog anchor point. - -/// \ingroup error -#define ORIGIN_ERROR_IGO_ILLEGAL_DOCK_POINT (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_IGO + 3) ///< The combination of dock point bits doesn't resolve to a proper dock point. - -/// \ingroup error -#define ORIGIN_ERROR_IGO_NOT_AVAILABLE (ORIGIN_ERROR + ORIGIN_LEVEL_3 + ORIGIN_ERROR_AREA_IGO + 4) ///< The IGO is not available. - -/// @} - -/// \name Presence Error Codes -/// These defines specify Origin Presence errors. -/// @{ - -/// \ingroup error -#define ORIGIN_ERROR_NO_MULTIPLAYER_ID (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PRESENCE + 0) ///< It is not possible to set the presence to JOINABLE when no multiplayer Id is defined on the offer. - -/// @} - -/// \name Friends Error Codes -/// These defines specify Origin Friends errors. -/// @{ - -/// \ingroup error -#define ORIGIN_ERROR_LSX_INVALID_RESPONSE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_LSX + 0) ///< The LSX Decoder didn't expect this response. - -/// \ingroup error -#define ORIGIN_ERROR_LSX_NO_RESPONSE (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_LSX + 1) ///< The LSX server didn't respond within the set timeout. - -/// \ingroup error -#define ORIGIN_ERROR_LSX_INVALID_REQUEST (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_LSX + 2) ///< The LSX Decoder didn't expect this request. - -/// @} - -/// \name Commerce Error Codes -/// These defines specify Origin Commerce errors. -/// @{ - -/// \ingroup error -#define ORIGIN_ERROR_COMMERCE_NO_SUCH_STORE (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_COMMERCE + 0) ///< The store could not be found. - -/// \ingroup error -#define ORIGIN_ERROR_COMMERCE_NO_SUCH_CATALOG (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_COMMERCE + 1) ///< The catalog could not be found. - -/// \ingroup error -#define ORIGIN_ERROR_COMMERCE_INVALID_REPLY (ORIGIN_ERROR + ORIGIN_LEVEL_1 + ORIGIN_ERROR_AREA_COMMERCE + 2) ///< The server reply was not understood. - -/// \ingroup error -#define ORIGIN_ERROR_COMMERCE_NO_CATEGORIES (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 3) ///< No categories were found. - -/// \ingroup error -#define ORIGIN_ERROR_COMMERCE_NO_PRODUCTS (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 4) ///< No products were found. - -/// \ingroup error -#define ORIGIN_ERROR_COMMERCE_UNDERAGE_USER (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 5) ///< The user is under age and is blocked to perform this action. - -/// \ingroup error -#define ORIGIN_ERROR_COMMERCE_DEPRECATED_STORE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_COMMERCE + 6) ///< The user's OS is deprecated and is blocked to perform this action. - - -/// @} - - -/// \name Origin Proxy Error Codes. -/// These defines specify Origin Proxy errors. -/// @{ -/// \ingroup error -#define ORIGIN_ERROR_PROXY (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 0) ///< Base proxy error. You shouldn't get this error. - -/// \ingroup error -#define ORIGIN_SUCCESS_PROXY_OK (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 200) ///< Server success: OK. - -/// \ingroup error -#define ORIGIN_SUCCESS_PROXY_CREATED (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 201) ///< Server success: Created. - -/// \ingroup error -#define ORIGIN_SUCCESS_PROXY_ACCEPTED (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 202) ///< Server success: Accepted. - -/// \ingroup error -#define ORIGIN_SUCCESS_PROXY_NON_AUTH_INFO (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 203) ///< Server success: Non-Authoritative Information. - -/// \ingroup error -#define ORIGIN_SUCCESS_PROXY_NO_CONTENT (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 204) ///< Server success: No Content. - -/// \ingroup error -#define ORIGIN_SUCCESS_RESET_CONTENT (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 205) ///< Server success: Reset Content. - -/// \ingroup error -#define ORIGIN_SUCCESS_PARTIAL_CONTENT (ORIGIN_WARNING + ORIGIN_LEVEL_4 + ORIGIN_ERROR_AREA_PROXY + 206) ///< Server success: Partial Content. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_BAD_REQUEST (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 400) ///< Server error: Bad Request - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_UNAUTHORIZED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 401) ///< Server error: Unauthorized. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_PAYMENT_REQUIRED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 402) ///< Server error: Payment Required. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_FORBIDDEN (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 403) ///< Server error: Forbidden. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_NOT_FOUND (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 404) ///< Server error: Not found. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_METHOD_NOT_ALLOWED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 405) ///< Server error: Method not Allowed. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_NOT_ACCEPTABLE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 406) ///< Server error: Not Acceptable. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_REQUEST_TIMEOUT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 408) ///< Server error: Request Timeout. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_CONFLICT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 409) ///< Server error: Conflict. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_INTERNAL_ERROR (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 500) ///< Server error: Internal Server Error. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_NOT_IMPLEMENTED (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 501) ///< Server error: Not Implemented. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_BAD_GATEWAY (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 502) ///< Server error: Bad Gateway. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_SERVICE_UNAVAILABLE (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 503) ///< Server error: Service Unavailable. - -/// \ingroup error -#define ORIGIN_ERROR_PROXY_GATEWAY_TIMEOUT (ORIGIN_ERROR + ORIGIN_LEVEL_2 + ORIGIN_ERROR_AREA_PROXY + 504) ///< Server error: Gateway Timeout. diff --git a/r5dev/include/gameclasses.h b/r5dev/include/gameclasses.h deleted file mode 100644 index 61c39cd4..00000000 --- a/r5dev/include/gameclasses.h +++ /dev/null @@ -1,521 +0,0 @@ -#pragma once -#include "patterns.h" -#include "banlist.h" - -///////////////////////////////////////////////////////////////////////////// -// Classes and Structs - -class CInputSystem -{ -public: - void EnableInput(bool bEnabled)// @0x14039F100 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CInputSystem*, bool); - (*reinterpret_cast(this))[10](this, bEnabled); - } - - void EnableMessagePump(bool bEnabled) // @0x14039F110 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CInputSystem*, bool); - (*reinterpret_cast(this))[11](this, bEnabled); - } - - bool IsButtonDown(ButtonCode_t Button) // @0x1403A0140 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = bool(__thiscall*)(CInputSystem*, ButtonCode_t); - return (*reinterpret_cast(this))[13](this, Button); - } - -private: - char pad_0000[16]; //0x0000 -public: - bool m_bEnabled; //0x0010 IsInputEnabled variable. - bool m_bPumpEnabled; //0x0011 EnabledMessagePump variable. -}; - -typedef int HKeySymbol; - -#define MAKE_3_BYTES_FROM_1_AND_2( x1, x2 ) (( (( std::uint16_t )x2) << 8 ) | (std::uint8_t)(x1)) - -class CKeyValuesSystem // VTABLE @ 0x1413AA1E8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM -{ -public: - - void RegisterSizeofKeyValues(std::int64_t size) //@0x1413AA1F0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, std::int64_t); - (*reinterpret_cast(this))[0](this, size); - } - - void* AllocKeyValuesMemory(std::int64_t size) // @0x1413AA1F8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void* (__thiscall*)(CKeyValuesSystem*, std::int64_t); - return (*reinterpret_cast(this))[1](this, size); - } - - void FreeKeyValuesMemory(void* pMem) // @0x1413AA200 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, void*); - (*reinterpret_cast(this))[2](this, pMem); - } - - HKeySymbol GetSymbolForString(const char* name, bool bCreate) // @0x1413AA208 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = HKeySymbol(__thiscall*)(CKeyValuesSystem*, const char*, bool); - return (*reinterpret_cast(this))[3](this, name, bCreate); - } - - const char* GetStringForSymbol(HKeySymbol symbol) // @0x1413AA210 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = const char* (__thiscall*)(CKeyValuesSystem*, HKeySymbol); - return (*reinterpret_cast(this))[4](this, symbol); - } - - // void __fastcall CKeyValuesSystem::FreeKeyValuesMemory(CKeyValuesSystem* this_arg, void* ptr_mem_arg) - // { - // __int64* v2; // rax - // __int64 v4; // rax - // __int64* v5; // rax - // - // v2 = qword_14D40B538; - // if (!qword_14D40B538) - // { - // v2 = sub_140462930(); - // qword_14D40B538 = v2; - // } - // v4 = (*(*v2 + 48))(v2, ptr_mem_arg); - // if (v4 > 0) - // CKeyValuesSystem::m_pMemPool -= v4; - // v5 = qword_14D40B538; - // if (!qword_14D40B538) - // { - // v5 = sub_140462930(); - // qword_14D40B538 = v5; - // } - // (*(*v5 + 40))(v5, ptr_mem_arg); - // } - - // GetMemPool return a global variable called m_pMemPool it gets modified by AllocKeyValuesMemory and FreeKeyValuesMemory above you can see where the find it in FreeKeyValuesMemory. - void* GetMemPool() // @0x1413AA228 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - return reinterpret_cast(0x14D412768); // May need to dereference is once more not sure right now. - } - - void SetKeyValuesExpressionSymbol(const char* name, bool bValue) // @0x1413AA230 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, const char*, bool); - (*reinterpret_cast(this))[8](this, name, bValue); - } - - bool GetKeyValuesExpressionSymbol(const char* name) // @0x1413AA238 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = bool(__thiscall*)(CKeyValuesSystem*, const char*); - return (*reinterpret_cast(this))[9](this, name); - } - - HKeySymbol GetSymbolForStringCaseSensitive(HKeySymbol& hCaseInsensitiveSymbol, const char* name, bool bCreate) // @0x1413AA240 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = HKeySymbol(__thiscall*)(CKeyValuesSystem*, HKeySymbol&, const char*, bool); - return (*reinterpret_cast(this))[10](this, hCaseInsensitiveSymbol, name, bCreate); - } - - // Datatypes aren't accurate. But full fill the actual byte distance. -public: - void* vtable; // 0x0000 - std::int64_t m_iMaxKeyValuesSize; // 0x0008 -private: - char gap10[240]; // 0x0010 -public: - int m_KvConditionalSymbolTable; // 0x0100 -private: - char gap104[4]; // 0x0104 -public: - std::int64_t field_108; // 0x0108 -private: - char gap110[32]; // 0x0110 -public: - int m_mutex; // 0x0130 -}; - -class KeyValues -{ -public: - - KeyValues* FindKey(const char* keyName, bool bCreate) - { - static auto func = reinterpret_cast(addr_KeyValues_FindKey); - return func(this, keyName, bCreate); - } - - const char* GetName(); - - int GetInt(const char* keyName, int defaultValue) - { - KeyValues* dat = FindKey(keyName, false); - - if (!dat) - return defaultValue; - - switch (dat->m_iDataType) - { - case TYPE_STRING: - return atoi(dat->m_sValue); - case TYPE_FLOAT: - return static_cast(m_flValue()); - case TYPE_WSTRING: - return _wtoi(dat->m_wsValue); - case TYPE_UINT64: - return 0; - default: - return dat->m_iValue(); - } - - return defaultValue; - } - - void SetInt(const char* keyName, int iValue) - { - KeyValues* dat = FindKey(keyName, true); - if (dat) - { - dat->m_iValue() = iValue; - dat->m_iDataType = TYPE_INT; - } - } - - void SetFloat(const char* keyName, float flValue) - { - KeyValues* dat = FindKey(keyName, true); - if (dat) - { - dat->m_flValue() = flValue; - dat->m_iDataType = TYPE_FLOAT; - } - } - - // Compiler makes it so m_Value shares the offset spot with m_flValue that why we cast it like this. - float& m_flValue() - { - static __int32 offset = 0x18; - return *(float*)((std::uintptr_t)this + offset); - } - - int& m_iValue() - { - static __int32 offset = 0x18; - return *(int*)((std::uintptr_t)this + offset); - } - -public: - unsigned __int32 m_iKeyName : 24; // 0x0000 - unsigned __int32 m_iKeyNameCaseSensitive : 8; // 0x0003 - char* m_sValue; // 0x0008 - wchar_t* m_wsValue; // 0x0010 - int m_Value; // 0x0018 -private: - char gap1C[12]; // 0x0020 -public: - char m_iDataType; // 0x0028 - unsigned __int16 m_iKeyNameCaseSensitive2; // 0x002A - KeyValues* m_pPeer; // 0x0030 - KeyValues* m_pSub; // 0x0038 - KeyValues* m_pChain; // 0x0040 -}; - -struct Vector3 // Implement the proper class of this at some point. -{ - float x; // 0x0000 - float y; // 0x0004 - float z; // 0x0008 -}; - -struct QAngle // Implement the proper class of this at some point. -{ - float pitch; //0x0000 - float yaw; // 0x0004 - float roll; // 0x0008 -}; - -class CHostState -{ -public: - HostStates_t m_iCurrentState; //0x0000 - HostStates_t m_iNextState; //0x0004 - Vector3 m_vecLocation; //0x0008 - QAngle m_angLocation; //0x0014 - char m_levelName[64]; //0x0020 - char m_mapGroupName[256]; //0x0060 - char m_landMarkName[256]; //0x0160 - float m_flShortFrameTime; //0x0260 - bool m_bActiveGame; //0x0264 - bool m_bRememberLocation; //0x0265 - bool m_bBackgroundLevel; //0x0266 - bool m_bWaitingForConnection; //0x0267 - bool m_bSplitScreenConnect; //0x0268 - bool m_bGameHasShutDownAndFlushedMemory; //0x0269 - bool m_bWorkshopMapDownloadPending; //0x026A -}; - -class CHLClient -{ -public: - void FrameStageNotify(ClientFrameStage_t curStage) // @0x1405C0740 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void(__thiscall*)(CHLClient*, ClientFrameStage_t); - (*reinterpret_cast(this))[58](this, curStage); /* 48 83 EC 28 89 15 ? ? ? ? */ - } -}; - -class CClient -{ -public: - inline CClient* GetClientInstance(int index) - { - return (CClient*)(std::uintptr_t)(0x16073B200 + (index * (std::uintptr_t)0x4A4C0)); - } - - void*& GetNetChan() - { - return m_nNetChannel; - } -private: - char pad_0000[16]; //0x0000 -public: - int m_iUserID; //0x0010 -private: - char pad_0014[908]; //0x0014 -public: - void* m_nNetChannel; //0x03A0 -private: - char pad_03A8[8]; //0x03A8 -public: - int m_iSignonstate; //0x03B0 -private: - char pad_03B4[4]; //0x03B4 -public: - std::int64_t m_iOriginID; //0x03B8 -private: - char pad_03C0[303360]; //0x03C0 -}; - -class CCommand -{ -private: - enum - { - COMMAND_MAX_ARGC = 64, - COMMAND_MAX_LENGTH = 512, - }; - -public: - CCommand() = delete; - - inline int MaxCommandLength() - { - return COMMAND_MAX_LENGTH - 1; - } - - inline std::int64_t ArgC() const - { - return m_nArgc; - } - - inline const char** ArgV() const - { - return m_nArgc ? (const char**)m_ppArgv : NULL; - } - - inline const char* ArgS() const - { - return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : ""; - } - - inline const char* GetCommandString() const - { - return m_nArgc ? m_pArgSBuffer : ""; - } - - inline const char* Arg(int nIndex) const - { - // FIXME: Many command handlers appear to not be particularly careful - // about checking for valid argc range. For now, we're going to - // do the extra check and return an empty string if it's out of range - if (nIndex < 0 || nIndex >= m_nArgc) - return ""; - return m_ppArgv[nIndex]; - } - - inline const char* operator[](int nIndex) const - { - return Arg(nIndex); - } - -private: - std::int64_t m_nArgc; - std::int64_t m_nArgv0Size; - char m_pArgSBuffer[COMMAND_MAX_LENGTH]; - char m_pArgvBuffer[COMMAND_MAX_LENGTH]; - const char* m_ppArgv[COMMAND_MAX_ARGC]; -}; - -class ConCommandBase -{ -public: - void* m_pConCommandBaseVTable; //0x0000 - ConCommandBase* m_pNext; //0x0008 - bool m_bRegistered; //0x0010 -private: - char pad_0011[7]; //0x0011 -public: - const char* m_pszName; //0x0018 - const char* m_pszHelpString; //0x0020 -private: - char pad_0028[16]; //0x0028 -public: - int m_nFlags; //0x0038 -private: - char pad_003C[4]; //0x003C -}; //Size: 0x0038 - -class ConVar -{ -public: - ConCommandBase m_ConCommandBase; // 0x0000 - void* m_pConVarVTable; //0x0040 - ConVar* m_pParent; //0x0048 - const char* n_pszDefaultValue; //0x0050 - const char* m_pzsCurrentValue; //0x0058 - std::int64_t m_iStringLength; //0x0060 - float m_flValue; //0x0068 - int m_iValue; //0x006C - bool m_bHasMin; //0x0070 - float m_flMinValue; //0x0074 - bool m_bHasMax; //0x0078 - float m_flMaxValue; //0x007C - char pad_0080[32]; //0x0080 -}; //Size: 0x00A0 - -class CCVarIteratorInternal // Fully reversed table, just look at the virtual function table and rename the function. -{ -public: - virtual void SetFirst(void) = 0; //0 - virtual void Next(void) = 0; //1 - virtual bool IsValid(void) = 0; //2 - virtual ConCommandBase* Get(void) = 0; //3 -}; - -class CCVar -{ -public: - ConCommandBase* FindCommandBase(const char* szCommandName) // @0x1405983A0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = ConCommandBase*(__thiscall*)(CCVar*, const char*); - return (*reinterpret_cast(this))[14](this, szCommandName); - } - - ConVar* FindVar(const char* szVarName) // @0x1405983B0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = ConVar*(__thiscall*)(CCVar*, const char*); - return (*reinterpret_cast(this))[16](this, szVarName); - } - - void* /*Implement ConCommand class.*/ FindCommand(const char* szCommandName) // @0x1405983F0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = void*(__thiscall*)(CCVar*, const char*); - return (*reinterpret_cast(this))[18](this, szCommandName); - } - - CCVarIteratorInternal* FactoryInternalIterator() // @0x140597C10 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - { - using OriginalFn = CCVarIteratorInternal*(__thiscall*)(CCVar*); - return (*reinterpret_cast(this))[41](this); - } - - std::unordered_map DumpToMap() - { - std::stringstream ss; - CCVarIteratorInternal* itint = FactoryInternalIterator(); // Allocatd new InternalIterator. - - std::unordered_map allConVars; - - for (itint->SetFirst(); itint->IsValid(); itint->Next()) // Loop through all instances. - { - ConCommandBase* command = itint->Get(); - const char* commandName = command->m_pszName; - allConVars[commandName] = command; - } - - return allConVars; - } -}; - -struct Interface -{ - std::int64_t(*InterfacePtr)(void); - const char* InterfaceName; - std::int64_t* NextInterfacePtr; -}; - -struct SQFuncRegistration -{ - const char* scriptName; // 00 - const char* nativeName; // 08 - const char* helpString; // 10 - const char* retValType; // 18 - const char* argTypes; // 20 - std::int16_t unk28; // 28 - std::int16_t padding1; // 2A - std::int32_t unk2c; // 2C - std::int64_t unk30; // 30 - std::int32_t unk38; // 38 - std::int32_t padding2; // 3C - std::int64_t unk40; // 40 - std::int64_t unk48; // 48 - std::int64_t unk50; // 50 - std::int32_t unk58; // 58 - std::int32_t padding3; // 5C - void* funcPtr; // 60 - - SQFuncRegistration() - { - memset(this, 0, sizeof(SQFuncRegistration)); - this->padding2 = 6; - } -}; - -///////////////////////////////////////////////////////////////////////////// -// Initialize Game Globals - -namespace GameGlobals -{ - // Class Instances - extern CHostState* HostState; - extern CInputSystem* InputSystem; - extern CCVar* Cvar; - extern KeyValues** PlaylistKeyValues; - extern CKeyValuesSystem* KeyValuesSystem; - extern CClient* Client; - extern BanList* BanSystem; - - // Var - ConVar* CreateCustomConVar(const char* name, const char* defaultValue, int flags, const char* helpString, bool bMin, float fMin, bool bMax, float fMax, void* callback, void* unk); - void* CreateCustomConCommand(const char* name, const char* helpString, int flags, void* callback, void* callbackAfterExecution); - - // Script - void Script_RegisterFunction(void* sqvm, const char* name, const char* helpString, const char* retValType, const char* argTypes, void* funcPtr); - void RegisterUIScriptFunctions(void* sqvm); - void RegisterClientScriptFunctions(void* sqvm); - void RegisterServerScriptFunctions(void* sqvm); - - // Init - void InitGameGlobals(); - void InitAllCommandVariations(); - void InitPlaylist(); - - - extern std::vector allPlaylists; - extern bool IsInitialized; - - // Utility - void DisconnectClient(CClient* client, const char* reason, unsigned __int8 unk1, char unk2); -} \ No newline at end of file diff --git a/r5dev/include/gui_utility.h b/r5dev/include/gui_utility.h deleted file mode 100644 index 2f1db191..00000000 --- a/r5dev/include/gui_utility.h +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once - -///////////////////////////////////////////////////////////////////////////// -// Internals -int Stricmp(const char* s1, const char* s2); -int Strnicmp(const char* s1, const char* s2, int n); -char* Strdup(const char* s); -void Strtrim(char* s); - -class GuiConfig -{ -public: - struct - { - int bind1 = VK_OEM_3; - int bind2 = VK_INSERT; - int autoClearLimit = 300; - bool autoClear = true; - bool printCmd = false; - } CGameConsoleConfig; - - struct - { - int bind1 = VK_HOME; - int bind2 = VK_F10; - } CCompanionConfig; - - void Load() - { - spdlog::debug("Loading the Gui Config..\n"); - std::filesystem::path path = std::filesystem::current_path() /= "gui.config"; // Get current path + gui.config - - nlohmann::json in; - try - { - std::ifstream configFile(path, std::ios::binary); // Parse config file. - configFile >> in; - configFile.close(); - - if (!in.is_null()) - { - if (!in["config"].is_null()) - { - // CGameConsole - CGameConsoleConfig.bind1 = in["config"]["CGameConsole"]["bind1"].get(); - CGameConsoleConfig.bind2 = in["config"]["CGameConsole"]["bind2"].get(); - CGameConsoleConfig.autoClearLimit = in["config"]["CGameConsole"]["autoClearLimit"].get(); - CGameConsoleConfig.autoClear = in["config"]["CGameConsole"]["autoClear"].get(); - CGameConsoleConfig.printCmd = in["config"]["CGameConsole"]["printCmd"].get(); - - // CCompanion - CCompanionConfig.bind1 = in["config"]["CCompanion"]["bind1"].get(); - CCompanionConfig.bind2 = in["config"]["CCompanion"]["bind2"].get(); - } - } - } - catch (const std::exception& ex) - { - spdlog::critical("Gui Config loading failed. Perhaps re-create it by messing with Options in CGameConsole. Reason: {}\n", ex.what()); - return; - } - } - - void Save() - { - nlohmann::json out; - - // CGameConsole - out["config"]["CGameConsole"]["bind1"] = CGameConsoleConfig.bind1; - out["config"]["CGameConsole"]["bind2"] = CGameConsoleConfig.bind2; - out["config"]["CGameConsole"]["autoClearLimit"] = CGameConsoleConfig.autoClearLimit; - out["config"]["CGameConsole"]["autoClear"] = CGameConsoleConfig.autoClear; - out["config"]["CGameConsole"]["printCmd"] = CGameConsoleConfig.printCmd; - - // CCompanion - out["config"]["CCompanion"]["bind1"] = CCompanionConfig.bind1; - out["config"]["CCompanion"]["bind2"] = CCompanionConfig.bind2; - - std::filesystem::path path = std::filesystem::current_path() /= "gui.config"; // Get current path + gui.config - std::ofstream outFile(path, std::ios::out | std::ios::trunc); // Write config file.. - - outFile << out.dump(4); // Dump it into config file.. - - outFile.close(); // Close the file handle. - }; -}; - -extern GuiConfig g_GuiConfig; diff --git a/r5dev/include/hooks.h b/r5dev/include/hooks.h deleted file mode 100644 index a6f7f1a2..00000000 --- a/r5dev/include/hooks.h +++ /dev/null @@ -1,155 +0,0 @@ -#pragma once -#include "patterns.h" -#include "structs.h" -#include "hooks.h" -#include "gameclasses.h" -#include "CCompanion.h" -#include "CGameConsole.h" - -inline bool g_bDebugLoading = false; -inline bool g_bReturnAllFalse = false; -inline bool g_bDebugConsole = false; -extern bool g_bBlockInput; - -namespace Hooks -{ -#pragma region CHLClient - void __fastcall FrameStageNotify(CHLClient* rcx, ClientFrameStage_t curStage); - - using FrameStageNotifyFn = void(__fastcall*)(CHLClient*, ClientFrameStage_t); - extern FrameStageNotifyFn originalFrameStageNotify; -#pragma endregion - -#pragma region Squirrel - void* SQVM_Print(void* sqvm, char* fmt, ...); - __int64 SQVM_Warning(void* sqvm, int a2, int a3, int* stringSize, void** string); - __int64 SQVM_LoadRson(const char* rson_name); - bool SQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag); - void SQVM_RegisterOriginFuncs(void* sqvm); - void SQVM_RegisterCreatePlayerTasklist(void* sqvm); - - using SQVM_WarningFn = __int64(*)(void*, int, int, int*, void**); - extern SQVM_WarningFn originalSQVM_Warning; - - using SQVM_LoadRsonFn = __int64(*)(const char*); - extern SQVM_LoadRsonFn originalSQVM_LoadRson; - - using SQVM_LoadScriptFn = bool(*)(void*, const char*, const char*, int); - extern SQVM_LoadScriptFn originalSQVM_LoadScript; - - using SQVM_RegisterOriginFuncsFn = void(*)(void*); - extern SQVM_RegisterOriginFuncsFn originalSQVM_RegisterOriginFuncs; - - using SQVM_RegisterCreatePlayerTasklistFn = void(*)(void*); - extern SQVM_RegisterCreatePlayerTasklistFn originalSQVM_RegisterCreatePlayerTasklist; -#pragma endregion - -#pragma region CServer - void* ConnectClient(void* thisptr, void* packet); - - using ConnectClientFn = void* (*)(void*, void*); - extern ConnectClientFn originalConnectClient; -#pragma endregion - -#pragma region CVEngineServer - bool IsPersistenceDataAvailable(__int64 thisptr, int client); - - using IsPersistenceDataAvailableFn = bool(*)(__int64, int); - extern IsPersistenceDataAvailableFn originalIsPersistenceDataAvailable; -#pragma endregion - -#pragma region NetChannel - bool NET_ReceiveDatagram(int sock, void* inpacket, bool raw); - unsigned int NET_SendDatagram(SOCKET s, const char* buf, int len, int flags); - void NET_PrintFunc(const char* fmt, ...); - void NetChan_Shutdown(void* rcx, const char* reason, unsigned __int8 unk1, char unk2); - - using NET_PrintFuncFn = void(*)(const char* fmt, ...); - extern NET_PrintFuncFn originalNET_PrintFunc; - - using NET_ReceiveDatagramFn = bool(*)(int, void*, bool); - extern NET_ReceiveDatagramFn originalNET_ReceiveDatagram; - - using NET_SendDatagramFn = unsigned int(*)(SOCKET, const char*, int, int); - extern NET_SendDatagramFn originalNET_SendDatagram; - using NetChan_ShutDown = void(*)(void*, const char*, unsigned __int8, char); - extern NetChan_ShutDown originalNetChan_ShutDown; -#pragma endregion - -#pragma region ConVar - bool ConVar_IsFlagSet(ConVar* cvar, int flag); - bool ConCommand_IsFlagSet(ConCommandBase* cmd, int flag); - - using ConVar_IsFlagSetFn = bool(*)(ConVar*, int); - extern ConVar_IsFlagSetFn originalConVar_IsFlagSet; - - using ConCommand_IsFlagSetFn = bool(*)(ConCommandBase*, int); - extern ConCommand_IsFlagSetFn originalConCommand_IsFlagSet; -#pragma endregion - -#pragma region CMatSystemSurface - void LockCursor(void* thisptr); - - using LockCursorFn = void(*)(void*); - extern LockCursorFn originalLockCursor; -#pragma endregion - -#pragma region WinAPI - BOOL WINAPI GetCursorPos(LPPOINT lpPoint); - BOOL WINAPI SetCursorPos(int X, int Y); - BOOL WINAPI ClipCursor(const RECT* lpRect); - BOOL WINAPI ShowCursor(BOOL bShow); - - using GetCursorPosFn = BOOL(WINAPI*)(LPPOINT); - extern GetCursorPosFn originalGetCursorPos; - - using SetCursorPosFn = BOOL(WINAPI*)(int, int); - extern SetCursorPosFn originalSetCursorPos; - - using ClipCursorFn = BOOL(WINAPI*)(const RECT*); - extern ClipCursorFn originalClipCursor; - - using ShowCursorFn = BOOL(WINAPI*)(BOOL); - extern ShowCursorFn originalShowCursor; -#pragma endregion - -#pragma region HostState - void FrameUpdate(void* rcx, void* rdx, float time); - - using FrameUpdateFn = void(*)(void*, void*, float); - extern FrameUpdateFn originalFrameUpdate; -#pragma endregion - -#pragma region CEngineVGui - int CEngineVGui_Paint(void* thisptr, int mode); - - using CEngineVGui_PaintFn = int(*)(void*, int); - extern CEngineVGui_PaintFn originalCEngineVGui_Paint; -#pragma endregion - -#pragma region OriginSDK - const char* OriginGetErrorDescription(std::uint32_t originCode); - - using OriginGetErrorDescriptionWrapperFn = const char* (*)(std::uint32_t); - extern OriginGetErrorDescriptionWrapperFn originalOriginGetErrorDescriptionWrapper; -#pragma endregion - -#pragma region Other - int MSG_EngineError(char* fmt, va_list args); - bool LoadPlaylist(const char* playlist); - - using MSG_EngineErrorFn = int(*)(char*, va_list); - extern MSG_EngineErrorFn originalMSG_EngineError; - - using LoadPlaylistFn = bool(*)(const char*); - extern LoadPlaylistFn originalLoadPlaylist; - -#pragma endregion - - void InstallHooks(); - void RemoveHooks(); - void ToggleNetTrace(); - extern bool bToggledNetTrace; - void ToggleDevCommands(); - extern bool bToggledDevFlags; -} \ No newline at end of file diff --git a/r5dev/include/id3dx.h b/r5dev/include/id3dx.h deleted file mode 100644 index cab22d1e..00000000 --- a/r5dev/include/id3dx.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -///////////////////////////////////////////////////////////////////////////// -// Initialization -void SetupDXSwapChain(); - -///////////////////////////////////////////////////////////////////////////// -// Internals -void PrintDXAddress(); -void InstallDXHooks(); -void RemoveDXHooks(); - -///////////////////////////////////////////////////////////////////////////// -// Handlers -extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -extern HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags); -extern HRESULT __stdcall GetResizeBuffers(IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags); - -///////////////////////////////////////////////////////////////////////////// -// Globals -extern DWORD g_dThreadId; -extern bool g_bShowConsole; -extern bool g_bShowBrowser; - -///////////////////////////////////////////////////////////////////////////// -// Utils -bool LoadTextureFromByteArray(unsigned char* image_data, const int& image_width, const int& image_height, ID3D11ShaderResourceView** out_srv); \ No newline at end of file diff --git a/r5dev/include/input.h b/r5dev/include/input.h deleted file mode 100644 index 7b9637ef..00000000 --- a/r5dev/include/input.h +++ /dev/null @@ -1 +0,0 @@ -#pragma once \ No newline at end of file diff --git a/r5dev/include/logsystem.h b/r5dev/include/logsystem.h deleted file mode 100644 index 3611ecc3..00000000 --- a/r5dev/include/logsystem.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -enum class LogType_t : int -{ - SCRIPT_SERVER, - SCRIPT_CLIENT, - SCRIPT_UI, - SCRIPT_WARNING, - NATIVE -}; - -struct Log -{ - Log(const std::string Message, const int Ticks, const LogType_t Type) - { - this->Message = Message; - this->Ticks = Ticks; - this->Type = Type; - } - std::string Message = ""; - int Ticks = 1024; - LogType_t Type = LogType_t::NATIVE; -}; - -class LogSystem -{ -public: - void AddLog(LogType_t type, std::string text); - void Update(); - -private: - std::array GetLogColorForType(LogType_t type); - std::vector m_vLogs; -}; - -extern LogSystem g_LogSystem; \ No newline at end of file diff --git a/r5dev/include/opcptc.h b/r5dev/include/opcptc.h deleted file mode 100644 index fc0715e2..00000000 --- a/r5dev/include/opcptc.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -void InstallOpcodes(); -inline HANDLE GameProcess = GetCurrentProcess(); - -namespace -{ - Module r5_op = Module("r5apex.exe"); // Create module class instance. - -#pragma region Origin - /*0x14032EEA0*/ - MemoryAddress Origin_Init = r5_op.PatternSearch("48 83 EC 28 80 3D ? ? ? 23 ? 0F 85 ? 02 ?"); - - /*0x140330290*/ - MemoryAddress Origin_SetState = r5_op.PatternSearch("48 81 EC 58 04 ? ? 80 3D ? ? ? ? ? 0F 84"); -#pragma endregion - -#pragma region Engine - /*0x14043FB90*/ - MemoryAddress dst002 = r5_op.PatternSearch("48 89 4C 24 08 56 41 55 48 81 EC 68 03 ? ? 4C"); - - /*0x14022A4A0*/ - MemoryAddress dst004 = r5_op.PatternSearch("48 83 EC 38 0F 29 74 24 20 48 89 5C 24 40 48 8B"); - - /*0x140238DA0*/ - MemoryAddress Host_NewGame = r5_op.PatternSearch("48 8B C4 ? 41 54 41 ? 48 81 EC ? ? ? ? F2"); -#pragma endregion - -#pragma region NetChannel - /*0x14030D000*/ - MemoryAddress CServer_Auth = r5_op.PatternSearch("40 55 57 41 55 41 57 48 8D AC 24 ? ? ? ?"); - /*0x1413336F0*/ - MemoryAddress NetChan_EncKey_DefaultAssign = r5_op.PatternSearch("E8 ? ? ? ? 48 8D 1D ? ? ? ? 4C 39 3D ? ? ? ?"); -#pragma endregion - -#pragma region FairFight - /*0x140303AE0*/ - MemoryAddress FairFight_Init = r5_op.PatternSearch("40 53 48 83 EC 20 8B 81 B0 03 ? ? 48 8B D9 C6"); -#pragma endregion - -#pragma region Squirrel - /*0x14105CCA0*/ - MemoryAddress Squirrel_CompileError = r5_op.StringSearch("%s SCRIPT COMPILE ERROR: %s").FindPatternSelf("48 89 5C", MemoryAddress::Direction::UP); -#pragma endregion - - void PrintOAddress() // Test the sigscan results - { - std::cout << "+--------------------------------------------------------+" << std::endl; - PRINT_ADDRESS("Origin_Init", Origin_Init.GetPtr()); - PRINT_ADDRESS("Origin_SetState", Origin_SetState.GetPtr()); - PRINT_ADDRESS("dst002", dst002.GetPtr()); - PRINT_ADDRESS("dst004", dst004.GetPtr()); - PRINT_ADDRESS("Host_NewGame", Host_NewGame.GetPtr()); - PRINT_ADDRESS("CServer_Auth", CServer_Auth.GetPtr()); - PRINT_ADDRESS("NetChan_EncKey_DefaultAssign", NetChan_EncKey_DefaultAssign.GetPtr()); - PRINT_ADDRESS("FairFight_Init", FairFight_Init.GetPtr()); - PRINT_ADDRESS("Squirrel_CompileError", Squirrel_CompileError.GetPtr()); - std::cout << "+--------------------------------------------------------+" << std::endl; - } -} diff --git a/r5dev/include/patterns.h b/r5dev/include/patterns.h deleted file mode 100644 index d2a20430..00000000 --- a/r5dev/include/patterns.h +++ /dev/null @@ -1,210 +0,0 @@ -#pragma once - -// Define the signatures or offsets to be searched and hooked -namespace -{ - Module r5_patterns = Module("r5apex.exe"); // Create module class instance. - -#pragma region Console - /*0x140202090*/ - FUNC_AT_ADDRESS(addr_CommandExecute, void(*)(void*, const char*), r5_patterns.PatternSearch("48 89 5C 24 ? 57 48 83 EC 20 48 8D 0D ? ? ? ? 41 8B D8").GetPtr()); - - /*0x14046FE90*/ - FUNC_AT_ADDRESS(addr_ConVar_IsFlagSet, bool(*)(int**, int), r5_patterns.PatternSearch("48 8B 41 48 85 50 38").GetPtr()); - - /*0x14046F490*/ - FUNC_AT_ADDRESS(addr_ConCommand_IsFlagSet, bool(*)(int*, int), r5_patterns.PatternSearch("85 51 38 0F 95 C0 C3").GetPtr()); - - /*0x140279CE0*/ - FUNC_AT_ADDRESS(addr_downloadPlaylists_Callback, void(*)(), r5_patterns.PatternSearch("33 C9 C6 05 ? ? ? ? ? E9 ? ? ? ?").GetPtr()); -#pragma endregion - -#pragma region Squirrel - /*0x141057FD0*/ - FUNC_AT_ADDRESS(addr_SQVM_Print, void*, r5_patterns.PatternSearch("83 F8 01 48 8D 3D ? ? ? ?").OffsetSelf(0x3).FollowNearCallSelf(0x3, 0x7).GetPtr()); - - /*0x14105F950*/ - FUNC_AT_ADDRESS(addr_SQVM_Warning, __int64(*)(__int64, int, int, const char*, std::size_t*), r5_patterns.PatternSearch("E8 ? ? ? ? 85 C0 0F 99 C3").FollowNearCallSelf().GetPtr()); - - /*0x140B1E55*/ - FUNC_AT_ADDRESS(addr_SQVM_Warning_ReturnAddr, void*, r5_patterns.PatternSearch("E8 ? ? ? ? 85 C0 0F 99 C3").OffsetSelf(0x5).GetPtr()); - - /*0x141061A50*/ - FUNC_AT_ADDRESS(addr_sq_pushstring, void(*)(void*, char*, __int64), r5_patterns.PatternSearch("E8 ? ? ? ? 8D 55 FE").FollowNearCall().GetPtr()); - - /*0x141061CD0*/ - FUNC_AT_ADDRESS(addr_sq_pushbool, void(*)(void*, int), r5_patterns.PatternSearch("E8 ? ? ? ? 41 0F B6 17").FollowNearCall().GetPtr()); - - /*0x141061C70*/ - FUNC_AT_ADDRESS(addr_sq_pushinteger, void(*)(void*, int), r5_patterns.PatternSearch("E8 ? ? ? ? 41 0F B7 17").FollowNearCall().GetPtr()); - - /*0x141062040*/ - FUNC_AT_ADDRESS(addr_sq_newarray, void(*)(void*, int), r5_patterns.PatternSearch("E8 ? ? ? ? 49 63 F5").FollowNearCall().GetPtr()); - - /*0x1410621F0*/ - FUNC_AT_ADDRESS(addr_sq_arrayappend, __int64(*)(void*, int), r5_patterns.PatternSearch("E8 ? ? ? ? 49 63 46 38").FollowNearCall().GetPtr()); - - /*0x141061FB0*/ - FUNC_AT_ADDRESS(addr_sq_newtable, void(*)(void*), r5_patterns.PatternSearch("E8 ? ? ? ? 83 C6 04").FollowNearCall().GetPtr()); - - /*0x141064250*/ - FUNC_AT_ADDRESS(addr_sq_newslot, __int64(*)(void*, int), r5_patterns.PatternSearch("E8 ? ? ? ? 49 63 46 18").FollowNearCall().GetPtr()); - - //DWORD64 p_SQVM_LoadScript = FindPattern("r5apex.exe", (const unsigned char*)"\x48\x89\x5C\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x48\x89\x4C\x24\x08\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\x6C", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); // For S0 and S1 - - /*0x141055630*/ - // For anything S2 and above (current S8 - FUNC_AT_ADDRESS(addr_SQVM_LoadScript, bool(*)(void*, const char*, const char*, int), r5_patterns.PatternSearch("48 8B C4 48 89 48 08 55 41 56 48 8D 68").GetPtr()); - - /*0x140C957E0*/ - FUNC_AT_ADDRESS(addr_SQVM_LoadRson, int(*)(const char*), r5_patterns.PatternSearch("4C 8B DC 49 89 5B 08 57 48 81 EC A0 00 00 00 33").GetPtr()); - - /*0x140834A00*/ - FUNC_AT_ADDRESS(addr_SQVM_RegisterOriginFuncs, void(*)(void*), r5_patterns.PatternSearch("E8 ? ? ? ? 48 8B 0D ? ? ? ? 48 8D 15 ? ? ? ? E8 ? ? ? ? 48 8B 05 ? ? ? ? C7 05 ? ? ? ? ? ? ? ?").FollowNearCall().GetPtr()); - - /*0x140C06B20*/ - FUNC_AT_ADDRESS(addr_SQVM_RegisterCreatePlayerTasklist, void(*)(void*), r5_patterns.PatternSearch("E8 ? ? ? ? 48 8B 0D ? ? ? ? E8 ? ? ? ? 48 8B CB").FollowNearCall().GetPtr()); -#pragma endregion - -#pragma region NetChannel - /*0x1402655F0*/ - FUNC_AT_ADDRESS(addr_NET_PrintFunc, bool(*)(int, void*, bool), r5_patterns.PatternSearch("48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 C3 48").GetPtr()); - - /*0x1402655F0*/ - FUNC_AT_ADDRESS(addr_NET_ReceiveDatagram, bool(*)(int, void*, bool), r5_patterns.PatternSearch("48 89 74 24 18 48 89 7C 24 20 55 41 54 41 55 41 56 41 57 48 8D AC 24 50 EB").GetPtr()); - - /*0x1402662D0*/ - FUNC_AT_ADDRESS(addr_NET_SendDatagram, int(*)(SOCKET, const char*, int, int), r5_patterns.PatternSearch("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ? 05 ? ?").GetPtr()); - - /*0x14025F190*/ - FUNC_AT_ADDRESS(addr_NetChan_Shutdown, void(*)(void*, const char*, unsigned __int8, char), r5_patterns.StringSearch("Disconnect by server.\n").FindPatternSelf("E8 ? ? ? ? 4C 89 B3 ? ? ? ?", MemoryAddress::Direction::DOWN).FollowNearCallSelf().GetPtr()); - - /*0x160686DC0*/ - MemoryAddress addr_NetChan_EncKeyPtr = r5_patterns.StringSearch("client:NetEncryption_NewKey").FindPatternSelf("48 8D ? ? ? ? ? 48 3B", MemoryAddress::Direction::UP, 150).ResolveRelativeAddressSelf(0x3, 0x7); - char* addr_NetChan_EncKey = addr_NetChan_EncKeyPtr.Offset(0x12D0).RCast(); - - /*0x140263E70*/ - FUNC_AT_ADDRESS(addr_NetChan_SetEncKey, void(*)(uintptr_t, const char*), MemoryAddress(0x140263E70).GetPtr()); - -#pragma endregion - -#pragma region CServer - /*0x140310230*/ - FUNC_AT_ADDRESS(addr_CServer_RejectConnection, void(*)(void*, unsigned int, void*, const char*), r5_patterns.StringSearch("#CONNECTION_FAILED_RESERVATION_TIMEOUT").FindPatternSelf("E8", MemoryAddress::Direction::DOWN).FollowNearCallSelf().GetPtr()); - - /*0x14030D000*/ - FUNC_AT_ADDRESS(addr_CServer_ConnectClient, void*(*)(void*, void*), r5_patterns.StringSearch("dedi.connect.fail.total:1|c\n").FindPatternSelf("E8", MemoryAddress::Direction::UP).FollowNearCallSelf().GetPtr()); -#pragma endregion - -#pragma region CHLClient - /*0x1405C0740*/ - FUNC_AT_ADDRESS(addr_CHLClient_FrameStageNotify, void(*)(void*, int), r5_patterns.PatternSearch("48 83 EC 28 89 15 ?? ?? ?? ??").GetPtr()); -#pragma endregion - -#pragma region CClient - /*0x140302FD0*/ - FUNC_AT_ADDRESS(addr_CClient_Clear, void(*)(__int64), r5_patterns.StringSearch("Disconnect by server.\n").FindPatternSelf("40", MemoryAddress::Direction::UP).GetPtr()); -#pragma endregion - -#pragma region CClientState - /*0x1418223E4*/ - FUNC_AT_ADDRESS(addr_m_bRestrictServerCommands, void*, r5_patterns.StringSearch("DevShotGenerator_Init()").FindPatternSelf("88 05", MemoryAddress::Direction::UP).ResolveRelativeAddressSelf(0x2).OffsetSelf(0x2).GetPtr()); -#pragma endregion - -#pragma region CVEngineServer - /*0x140315CF0*/ - FUNC_AT_ADDRESS(addr_CVEngineServer_IsPersistenceDataAvailable, bool(*)(__int64, int), r5_patterns.PatternSearch("3B 15 ?? ?? ?? ?? 7D 33").GetPtr()); -#pragma endregion - -#pragma region CBaseFileSystem - /*0x14038BE20*/ - FUNC_AT_ADDRESS(addr_CBaseFileSystem_FileSystemWarning, void(*)(void*, FileWarningLevel_t, const char*, ...), r5_patterns.PatternSearch("E8 ? ? ? ? 33 C0 80 3B 2A").FollowNearCallSelf().GetPtr()); -#pragma endregion - -#pragma region CMatSystemSurface - /*0x140548A00*/ - FUNC_AT_ADDRESS(addr_CMatSystemSurface_LockCursor, void(*)(void*), MemoryAddress(0x140548A00).GetPtr()); // Maybe sigscan this via RTTI. - - /*0x1405489C0*/ - FUNC_AT_ADDRESS(addr_CMatSystemSurface_UnlockCursor, void(*)(void*), MemoryAddress(0x1405489C0).GetPtr()); // Maybe sigscan this via RTTI. - - /*0x140547900*/ - FUNC_AT_ADDRESS(addr_CMatSystemSurface_DrawColoredText, void(*)(void*, int, int, int, int, int, int, int, int, const char*, ...), MemoryAddress(0x140547900).GetPtr()); -#pragma region Utility - /*0x140295600*/ - FUNC_AT_ADDRESS(addr_MSG_EngineError, int(*)(char*, va_list), r5_patterns.StringSearch("Engine Error").FindPatternSelf("48 89 ? ? ? 48 89", MemoryAddress::Direction::UP, 500).GetPtr()); - - /*0x1401B31C0*/ - FUNC_AT_ADDRESS(addr_MemAlloc_Wrapper, void*(*)(__int64), r5_patterns.StringSearch("ConversionModeMenu").FindPatternSelf("E8 ? ? ? ? 48", MemoryAddress::Direction::UP).FollowNearCallSelf().GetPtr()); - - /*0x14B37DE80 has current loaded playlist name*/ - /*0x1402790C0*/ - FUNC_AT_ADDRESS(addr_LoadPlaylist, bool(*)(const char*), r5_patterns.PatternSearch("E8 ? ? ? ? 80 3D ? ? ? ? ? 74 0C").FollowNearCallSelf().GetPtr()); - - /*0x1671060C0*/ - FUNC_AT_ADDRESS(addr_MapVPKCache, void*, r5_patterns.StringSearch("PrecacheMTVF").FindPatternSelf("48 8D 1D ? ? ? ? 4C", MemoryAddress::Direction::UP, 900).OffsetSelf(0x3).ResolveRelativeAddressSelf().GetPtr()); - - /*0x140278C50*/ - FUNC_AT_ADDRESS(addr_mp_gamemode_Callback, bool(*)(const char*), r5_patterns.StringSearch("Failed to load playlist data\n").FindPatternSelf("E8 ? ? ? ? B0 01", MemoryAddress::Direction::DOWN, 200).FollowNearCallSelf().GetPtr()); -#pragma endregion - -#pragma region KeyValues - /*0x1404744E0*/ - FUNC_AT_ADDRESS(addr_KeyValues_FindKey, void*(*)(void*, const char*, bool), r5_patterns.PatternSearch("40 56 57 41 57 48 81 EC ?? ?? ?? ?? 45").GetPtr()); -#pragma endregion - -#pragma region CEngineVGui - /*0x140283FD0*/ - FUNC_AT_ADDRESS(addr_CEngineVGui_Paint, int(*)(void*, int), r5_patterns.PatternSearch("41 55 41 56 48 83 EC 78 44 8B EA").GetPtr()); -#pragma endregion - -#pragma region OriginSDK - /*0x1410A2D70*/ - FUNC_AT_ADDRESS(addr_OriginGetErrorDescription, const char*(*)(std::int32_t), r5_patterns.StringSearch("OriginGetErrorDescription entered").FindPatternSelf("40 53", MemoryAddress::Direction::UP).GetPtr()); -#pragma endregion - - - void PrintHAddress() // Test the sigscan results - { - std::cout << "+--------------------------------------------------------+" << std::endl; - PRINT_ADDRESS("CommandExecute", addr_CommandExecute); - PRINT_ADDRESS("ConVar_IsFlagSet", addr_ConVar_IsFlagSet); - PRINT_ADDRESS("ConCommand_IsFlagSet", addr_ConCommand_IsFlagSet); - PRINT_ADDRESS("Downloadplaylists_Callback", addr_downloadPlaylists_Callback); - PRINT_ADDRESS("MP_gamemode_Callback", addr_mp_gamemode_Callback); - PRINT_ADDRESS("SQVM_Print", addr_SQVM_Print); - PRINT_ADDRESS("SQVM_LoadScript", addr_SQVM_LoadScript); - PRINT_ADDRESS("SQVM_LoadRson", addr_SQVM_LoadRson); - PRINT_ADDRESS("SQVM_Warning", addr_SQVM_Warning); - PRINT_ADDRESS("SQVM_Warning_ReturnAddr", addr_SQVM_Warning_ReturnAddr); - PRINT_ADDRESS("SQVM_RegisterOriginFuncs", addr_SQVM_RegisterOriginFuncs); - PRINT_ADDRESS("SQVM_RegisterCreatePlayerTasklist", addr_SQVM_RegisterCreatePlayerTasklist); - PRINT_ADDRESS("sq_arrayappend", addr_sq_arrayappend); - PRINT_ADDRESS("sq_newarray", addr_sq_newarray); - PRINT_ADDRESS("sq_newslot", addr_sq_newslot); - PRINT_ADDRESS("sq_newtable", addr_sq_newtable); - PRINT_ADDRESS("sq_pushbool", addr_sq_pushbool); - PRINT_ADDRESS("sq_pushinteger", addr_sq_pushinteger); - PRINT_ADDRESS("sq_pushstring", addr_sq_pushstring); - PRINT_ADDRESS("NET_PrintFunc", addr_NET_PrintFunc); - PRINT_ADDRESS("NET_ReceiveDatagram", addr_NET_ReceiveDatagram); - PRINT_ADDRESS("NET_SendDatagram ", addr_NET_SendDatagram); - PRINT_ADDRESS("CClientState::m_bRestrictServerCommands", addr_m_bRestrictServerCommands); - PRINT_ADDRESS("CClient::Clear", addr_CClient_Clear); - PRINT_ADDRESS("INetChannel::Shutdown", addr_NetChan_Shutdown); - PRINT_ADDRESS("INetChannel::SetEncryptionKey", addr_NetChan_SetEncKey); - PRINT_ADDRESS("INetChannel::EncryptionKey", addr_NetChan_EncKey); - PRINT_ADDRESS("CHLClient::FrameStageNotify", addr_CHLClient_FrameStageNotify); - PRINT_ADDRESS("CVEngineServer::IsPersistenceDataAvailable", addr_CVEngineServer_IsPersistenceDataAvailable); - PRINT_ADDRESS("CServer::ConnectClient", addr_CServer_ConnectClient); - PRINT_ADDRESS("CServer::RejectConnection", addr_CServer_RejectConnection); - PRINT_ADDRESS("CBaseFileSystem::FileSystemWarning", addr_CBaseFileSystem_FileSystemWarning); - PRINT_ADDRESS("MSG_EngineError", addr_MSG_EngineError); - PRINT_ADDRESS("LoadPlaylist", addr_LoadPlaylist); - PRINT_ADDRESS("MapVPKCache", addr_MapVPKCache); - PRINT_ADDRESS("MemAlloc_Wrapper", addr_MemAlloc_Wrapper); - PRINT_ADDRESS("KeyValues::FindKey", addr_KeyValues_FindKey); - std::cout << "+--------------------------------------------------------+" << std::endl; - // TODO implement error handling when sigscan fails or result is 0 - } -} diff --git a/r5dev/include/pch.h b/r5dev/include/pch.h deleted file mode 100644 index de793c13..00000000 --- a/r5dev/include/pch.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#pragma message("[DEV] pre-compiling headers.\n") - -#define WIN32_LEAN_AND_MEAN // Prevent winsock2 redefinition. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Our headers - -#include "imgui.h" -#include "imgui_stdlib.h" -#include "imgui_impl_dx11.h" -#include "imgui_impl_win32.h" -#include "spdlog.h" -#include "sinks/basic_file_sink.h" -#include "sinks/stdout_sinks.h" -#include "sinks/ostream_sink.h" -#include "utility.h" -#include "httplib.h" -#include "json.hpp" - -#include "address.h" -#include "enums.h" - -#define FUNC_AT_ADDRESS(name, funcbody, addr) \ - using _##name = funcbody; \ - _##name name = (funcbody)addr \ - -#define PRINT_ADDRESS(name, address) std::cout << name << ": " << std::hex << std::uppercase << address << std::endl; \ No newline at end of file diff --git a/r5dev/include/squirrel.h b/r5dev/include/squirrel.h deleted file mode 100644 index 568fb824..00000000 --- a/r5dev/include/squirrel.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -char* sq_getstring(void* sqvm, int i); -int sq_getinteger(void* sqvm, int i); - -void sq_pushbool(void* sqvm, int val); - -void sq_pushstring(void* sqvm, char* string, int len); -void sq_pushstring(void* sqvm, const char* string, int len); - -void sq_pushinteger(void* sqvm, int val); - -void sq_newarray(void* sqvm, int size); -void sq_arrayappend(void* sqvm, int idx); - -void sq_newtable(void* sqvm); -void sq_newslot(void* sqvm, int idx); \ No newline at end of file diff --git a/r5dev/include/structs.h b/r5dev/include/structs.h deleted file mode 100644 index f15e396d..00000000 --- a/r5dev/include/structs.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -/* Structures */ -typedef unsigned __int64 QWORD; - -struct __declspec(align(8)) netpacket_t -{ - DWORD family_maybe; - sockaddr_in sin; - WORD sin_port; - BYTE gap16; - BYTE byte17; - DWORD source; - double received; - unsigned __int8* data; - QWORD label; - BYTE byte38; - QWORD qword40; - QWORD qword48; - BYTE gap50[8]; - QWORD qword58; - QWORD qword60; - QWORD qword68; - int less_than_12; - DWORD wiresize; - BYTE gap78[8]; - QWORD qword80; -}; diff --git a/r5dev/inputsystem/ButtonCode.h b/r5dev/inputsystem/ButtonCode.h new file mode 100644 index 00000000..f622d024 --- /dev/null +++ b/r5dev/inputsystem/ButtonCode.h @@ -0,0 +1,215 @@ +#pragma once +#include "tier0/basetypes.h" + +#define JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_BUTTON + ((_joystick) * JOYSTICK_MAX_BUTTON_COUNT) + (_button) ) +#define JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_POV_BUTTON + ((_joystick) * JOYSTICK_POV_BUTTON_COUNT) + (_button) ) +#define JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_AXIS_BUTTON + ((_joystick) * JOYSTICK_AXIS_BUTTON_COUNT) + (_button) ) + +#define JOYSTICK_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ) +#define JOYSTICK_POV_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ) +#define JOYSTICK_AXIS_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ) + +// Buttons are not confirmed to be the same. They have been always the same throughout the source engine. Lets hope they did not change them. +enum +{ + MAX_JOYSTICKS = MAX_SPLITSCREEN_CLIENTS, + MOUSE_BUTTON_COUNT = 5, +}; + +enum JoystickAxis_t +{ + JOY_AXIS_X = 0, + JOY_AXIS_Y, + JOY_AXIS_Z, + JOY_AXIS_R, + JOY_AXIS_U, + JOY_AXIS_V, + MAX_JOYSTICK_AXES, +}; + +enum +{ + JOYSTICK_MAX_BUTTON_COUNT = 32, + JOYSTICK_POV_BUTTON_COUNT = 4, + JOYSTICK_AXIS_BUTTON_COUNT = MAX_JOYSTICK_AXES * 2, +}; + +//----------------------------------------------------------------------------- +// Button enum. "Buttons" are binary-state input devices (mouse buttons, keyboard keys) +//----------------------------------------------------------------------------- +enum ButtonCode_t +{ + BUTTON_CODE_INVALID = -1, + BUTTON_CODE_NONE = 0, + + KEY_FIRST = 0, + + KEY_NONE = KEY_FIRST, + KEY_0, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_A, + KEY_B, + KEY_C, + KEY_D, + KEY_E, + KEY_F, + KEY_G, + KEY_H, + KEY_I, + KEY_J, + KEY_K, + KEY_L, + KEY_M, + KEY_N, + KEY_O, + KEY_P, + KEY_Q, + KEY_R, + KEY_S, + KEY_T, + KEY_U, + KEY_V, + KEY_W, + KEY_X, + KEY_Y, + KEY_Z, + KEY_PAD_0, + KEY_PAD_1, + KEY_PAD_2, + KEY_PAD_3, + KEY_PAD_4, + KEY_PAD_5, + KEY_PAD_6, + KEY_PAD_7, + KEY_PAD_8, + KEY_PAD_9, + KEY_PAD_DIVIDE, + KEY_PAD_MULTIPLY, + KEY_PAD_MINUS, + KEY_PAD_PLUS, + KEY_PAD_ENTER, + KEY_PAD_DECIMAL, + KEY_LBRACKET, + KEY_RBRACKET, + KEY_SEMICOLON, + KEY_APOSTROPHE, + KEY_BACKQUOTE, + KEY_COMMA, + KEY_PERIOD, + KEY_SLASH, + KEY_BACKSLASH, + KEY_MINUS, + KEY_EQUAL, + KEY_ENTER, + KEY_SPACE, + KEY_BACKSPACE, + KEY_TAB, + KEY_CAPSLOCK, + KEY_NUMLOCK, + KEY_ESCAPE, + KEY_SCROLLLOCK, + KEY_INSERT, + KEY_DELETE, + KEY_HOME, + KEY_END, + KEY_PAGEUP, + KEY_PAGEDOWN, + KEY_BREAK, + KEY_LSHIFT, + KEY_RSHIFT, + KEY_LALT, + KEY_RALT, + KEY_LCONTROL, + KEY_RCONTROL, + KEY_LWIN, + KEY_RWIN, + KEY_APP, + KEY_UP, + KEY_LEFT, + KEY_DOWN, + KEY_RIGHT, + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, + KEY_CAPSLOCKTOGGLE, + KEY_NUMLOCKTOGGLE, + KEY_SCROLLLOCKTOGGLE, + + KEY_LAST = KEY_SCROLLLOCKTOGGLE, + KEY_COUNT = KEY_LAST - KEY_FIRST + 1, + + // Mouse + MOUSE_FIRST = KEY_LAST + 1, + + MOUSE_LEFT = MOUSE_FIRST, + MOUSE_RIGHT, + MOUSE_MIDDLE, + MOUSE_4, + MOUSE_5, + MOUSE_WHEEL_UP, // A fake button which is 'pressed' and 'released' when the wheel is moved up + MOUSE_WHEEL_DOWN, // A fake button which is 'pressed' and 'released' when the wheel is moved down + + MOUSE_LAST = MOUSE_WHEEL_DOWN, + MOUSE_COUNT = MOUSE_LAST - MOUSE_FIRST + 1, + + // Joystick + JOYSTICK_FIRST = MOUSE_LAST + 1, + + JOYSTICK_FIRST_BUTTON = JOYSTICK_FIRST, + JOYSTICK_LAST_BUTTON = JOYSTICK_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_MAX_BUTTON_COUNT - 1), + JOYSTICK_FIRST_POV_BUTTON, + JOYSTICK_LAST_POV_BUTTON = JOYSTICK_POV_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_POV_BUTTON_COUNT - 1), + JOYSTICK_FIRST_AXIS_BUTTON, + JOYSTICK_LAST_AXIS_BUTTON = JOYSTICK_AXIS_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_AXIS_BUTTON_COUNT - 1), + + JOYSTICK_LAST = JOYSTICK_LAST_AXIS_BUTTON, + + BUTTON_CODE_LAST, + BUTTON_CODE_COUNT = BUTTON_CODE_LAST - KEY_FIRST + 1, + + // Helpers for XBox 360 + KEY_XBUTTON_UP = JOYSTICK_FIRST_POV_BUTTON, // POV buttons + KEY_XBUTTON_RIGHT, + KEY_XBUTTON_DOWN, + KEY_XBUTTON_LEFT, + + KEY_XBUTTON_A = JOYSTICK_FIRST_BUTTON, // Buttons + KEY_XBUTTON_B, + KEY_XBUTTON_X, + KEY_XBUTTON_Y, + KEY_XBUTTON_LEFT_SHOULDER, + KEY_XBUTTON_RIGHT_SHOULDER, + KEY_XBUTTON_BACK, + KEY_XBUTTON_START, + KEY_XBUTTON_STICK1, + KEY_XBUTTON_STICK2, + KEY_XBUTTON_INACTIVE_START, + + KEY_XSTICK1_RIGHT = JOYSTICK_FIRST_AXIS_BUTTON, // XAXIS POSITIVE + KEY_XSTICK1_LEFT, // XAXIS NEGATIVE + KEY_XSTICK1_DOWN, // YAXIS POSITIVE + KEY_XSTICK1_UP, // YAXIS NEGATIVE + KEY_XBUTTON_LTRIGGER, // ZAXIS POSITIVE + KEY_XBUTTON_RTRIGGER, // ZAXIS NEGATIVE + KEY_XSTICK2_RIGHT, // UAXIS POSITIVE + KEY_XSTICK2_LEFT, // UAXIS NEGATIVE + KEY_XSTICK2_DOWN, // VAXIS POSITIVE + KEY_XSTICK2_UP, // VAXIS NEGATIVE +}; \ No newline at end of file diff --git a/r5dev/inputsystem/inputsystem.cpp b/r5dev/inputsystem/inputsystem.cpp new file mode 100644 index 00000000..02911bde --- /dev/null +++ b/r5dev/inputsystem/inputsystem.cpp @@ -0,0 +1,6 @@ +#include "core/stdafx.h" +#include "vpc/IAppSystem.h" +#include "inputsystem/inputsystem.h" + +/////////////////////////////////////////////////////////////////////////////// +CInputSystem* g_pInputSystem = reinterpret_cast(p_IAppSystem_LoadLibrary.FindPatternSelf("48 89 05", ADDRESS::Direction::DOWN, 40).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr()); diff --git a/r5dev/inputsystem/inputsystem.h b/r5dev/inputsystem/inputsystem.h new file mode 100644 index 00000000..96fb5f3c --- /dev/null +++ b/r5dev/inputsystem/inputsystem.h @@ -0,0 +1,47 @@ +#pragma once +#include "core/stdafx.h" +#include "inputsystem/ButtonCode.h" + +class CInputSystem +{ +public: + void EnableInput(bool bEnabled)// @0x14039F100 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CInputSystem*, bool); + (*reinterpret_cast(this))[10](this, bEnabled); + } + + void EnableMessagePump(bool bEnabled) // @0x14039F110 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CInputSystem*, bool); + (*reinterpret_cast(this))[11](this, bEnabled); + } + + bool IsButtonDown(ButtonCode_t Button) // @0x1403A0140 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = bool(__thiscall*)(CInputSystem*, ButtonCode_t); + return (*reinterpret_cast(this))[13](this, Button); + } + +private: + char pad_0000[16]; //0x0000 +public: + bool m_bEnabled; //0x0010 IsInputEnabled variable. + bool m_bPumpEnabled; //0x0011 EnabledMessagePump variable. +}; + +/////////////////////////////////////////////////////////////////////////////// +extern CInputSystem* g_pInputSystem +; +/////////////////////////////////////////////////////////////////////////////// +class HInputSystem : public IDetour +{ + virtual void debugp() + { + std::cout << "| VAR: g_pInputSystem : 0x" << std::hex << std::uppercase << g_pInputSystem << std::setw(0) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HInputSystem); diff --git a/r5dev/launcher/IApplication.cpp b/r5dev/launcher/IApplication.cpp new file mode 100644 index 00000000..5d5f5ee0 --- /dev/null +++ b/r5dev/launcher/IApplication.cpp @@ -0,0 +1,32 @@ +#include "core/stdafx.h" +#include "launcher/IApplication.h" +#include "ebisusdk/EbisuSDK.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void* HIApplication_Main(void* a1, void* a2) +{ + HEbisuSDK_Init(); + return IAppSystem_Main(a1, a2); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool HIApplication_Create(void* a1) +{ + return IAppSystem_Create(a1); +} + +void IApplication_Attach() +{ + DetourAttach((LPVOID*)&IAppSystem_Main, &HIApplication_Main); + DetourAttach((LPVOID*)&IAppSystem_Create, &HIApplication_Create); +} + +void IApplication_Detach() +{ + DetourDetach((LPVOID*)&IAppSystem_Main, &HIApplication_Main); + DetourDetach((LPVOID*)&IAppSystem_Create, &HIApplication_Create); +} diff --git a/r5dev/launcher/IApplication.h b/r5dev/launcher/IApplication.h new file mode 100644 index 00000000..598f04d8 --- /dev/null +++ b/r5dev/launcher/IApplication.h @@ -0,0 +1,37 @@ +#pragma once +#include "tier0/basetypes.h" + +namespace +{ +#if defined (GAMEDLL_S1) || defined (GAMEDLL_S1) + +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + /* ==== CAPPSYSTEMGROUP ================================================================================================================================================= */ + ADDRESS p_IAppSystem_Main = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x80\xB9\x00\x00\x00\x00\x00\xBB\x00\x00\x00\x00", "xxxxxxxx?????x????"); + void* (*IAppSystem_Main)(void* a1, void* a2) = (void* (*)(void*, void*))p_IAppSystem_Main.GetPtr(); /*40 53 48 83 EC 20 80 B9 ?? ?? ?? ?? ?? BB ?? ?? ?? ??*/ + + ADDRESS p_IAppSystem_Create = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\xC4\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8B\xEC\x48\x83\xEC\x60", "xxxxxxxxxxxxxxxxxxx"); + bool (*IAppSystem_Create)(void* a1) = (bool(*)(void*))p_IAppSystem_Create.GetPtr(); /*48 8B C4 55 41 54 41 55 41 56 41 57 48 8B EC 48 83 EC 60*/ +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +void* HIApplication_Main(void* a1, void* a2); +bool HIApplication_Create(void* a1); + +void IApplication_Attach(); +void IApplication_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HApplication : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: IAppSystem::Main : 0x" << std::hex << std::uppercase << p_IAppSystem_Main.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: IAppSystem::Create : 0x" << std::hex << std::uppercase << p_IAppSystem_Create.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HApplication); diff --git a/r5dev/mathlib/IceKey.H b/r5dev/mathlib/IceKey.H new file mode 100644 index 00000000..f8641d06 --- /dev/null +++ b/r5dev/mathlib/IceKey.H @@ -0,0 +1,62 @@ +// Purpose: Header file for the C++ ICE encryption class. +// Taken from public domain code, as written by Matthew Kwan - July 1996 +// http://www.darkside.com.au/ice/ + +#ifndef _IceKey_H +#define _IceKey_H + +/* +The IceKey class is used for encrypting and decrypting 64-bit blocks of data +with the ICE (Information Concealment Engine) encryption algorithm. + +The constructor creates a new IceKey object that can be used to encrypt and decrypt data. +The level of encryption determines the size of the key, and hence its speed. +Level 0 uses the Thin-ICE variant, which is an 8-round cipher taking an 8-byte key. +This is the fastest option, and is generally considered to be at least as secure as DES, +although it is not yet certain whether it is as secure as its key size. + +For levels n greater than zero, a 16n-round cipher is used, taking 8n-byte keys. +Although not as fast as level 0, these are very very secure. + +Before an IceKey can be used to encrypt data, its key schedule must be set with the set() member function. +The length of the key required is determined by the level, as described above. + +The member functions encrypt() and decrypt() encrypt and decrypt respectively data +in blocks of eight chracters, using the specified key. + +Two functions keySize() and blockSize() are provided +which return the key and block size respectively, measured in bytes. +The key size is determined by the level, while the block size is always 8. + +The destructor zeroes out and frees up all memory associated with the key. +*/ + +class IceSubkey; + +class IceKey { + public: + IceKey (int n); + ~IceKey (); + + void set (const unsigned char *key); + + void encrypt (const unsigned char *plaintext, + unsigned char *ciphertext) const; + + void decrypt (const unsigned char *ciphertext, + unsigned char *plaintext) const; + + int keySize () const; + + int blockSize () const; + + private: + void scheduleBuild (unsigned short *k, int n, + const int *keyrot); + + int _size; + int _rounds; + IceSubkey *_keysched; +}; + +#endif diff --git a/r5dev/mathlib/IceKey.cpp b/r5dev/mathlib/IceKey.cpp new file mode 100644 index 00000000..6ba93eda --- /dev/null +++ b/r5dev/mathlib/IceKey.cpp @@ -0,0 +1,392 @@ +// Purpose: C++ implementation of the ICE encryption algorithm. +// Taken from public domain code, as written by Matthew Kwan - July 1996 +// http://www.darkside.com.au/ice/ +#include "core/stdafx.h" +#include "mathlib/IceKey.H" +#if !defined(_STATIC_LINKED) || defined(_SHARED_LIB) + +#pragma warning(disable: 4244) + + + /* Structure of a single round subkey */ +class IceSubkey { + public: + unsigned long val[3]; +}; + + + /* The S-boxes */ +static unsigned long ice_sbox[4][1024]; +static int ice_sboxes_initialised = 0; + + + /* Modulo values for the S-boxes */ +static const int ice_smod[4][4] = { + {333, 313, 505, 369}, + {379, 375, 319, 391}, + {361, 445, 451, 397}, + {397, 425, 395, 505}}; + + /* XOR values for the S-boxes */ +static const int ice_sxor[4][4] = { + {0x83, 0x85, 0x9b, 0xcd}, + {0xcc, 0xa7, 0xad, 0x41}, + {0x4b, 0x2e, 0xd4, 0x33}, + {0xea, 0xcb, 0x2e, 0x04}}; + + /* Permutation values for the P-box */ +static const unsigned long ice_pbox[32] = { + 0x00000001, 0x00000080, 0x00000400, 0x00002000, + 0x00080000, 0x00200000, 0x01000000, 0x40000000, + 0x00000008, 0x00000020, 0x00000100, 0x00004000, + 0x00010000, 0x00800000, 0x04000000, 0x20000000, + 0x00000004, 0x00000010, 0x00000200, 0x00008000, + 0x00020000, 0x00400000, 0x08000000, 0x10000000, + 0x00000002, 0x00000040, 0x00000800, 0x00001000, + 0x00040000, 0x00100000, 0x02000000, 0x80000000}; + + /* The key rotation schedule */ +static const int ice_keyrot[16] = { + 0, 1, 2, 3, 2, 1, 3, 0, + 1, 3, 2, 0, 3, 1, 0, 2}; + + +/* + * 8-bit Galois Field multiplication of a by b, modulo m. + * Just like arithmetic multiplication, except that additions and + * subtractions are replaced by XOR. + */ + +static unsigned int +gf_mult ( + unsigned int a, + unsigned int b, + unsigned int m +) { + unsigned int res = 0; + + while (b) { + if (b & 1) + res ^= a; + + a <<= 1; + b >>= 1; + + if (a >= 256) + a ^= m; + } + + return (res); +} + + +/* + * Galois Field exponentiation. + * Raise the base to the power of 7, modulo m. + */ + +static unsigned long +gf_exp7 ( + unsigned int b, + unsigned int m +) { + unsigned int x; + + if (b == 0) + return (0); + + x = gf_mult (b, b, m); + x = gf_mult (b, x, m); + x = gf_mult (x, x, m); + return (gf_mult (b, x, m)); +} + + +/* + * Carry out the ICE 32-bit P-box permutation. + */ + +static unsigned long +ice_perm32 ( + unsigned long x +) { + unsigned long res = 0; + const unsigned long *pbox = ice_pbox; + + while (x) { + if (x & 1) + res |= *pbox; + pbox++; + x >>= 1; + } + + return (res); +} + + +/* + * Initialise the ICE S-boxes. + * This only has to be done once. + */ + +static void +ice_sboxes_init (void) +{ + int i; + + for (i=0; i<1024; i++) { + int col = (i >> 1) & 0xff; + int row = (i & 0x1) | ((i & 0x200) >> 8); + unsigned long x; + + x = gf_exp7 (col ^ ice_sxor[0][row], ice_smod[0][row]) << 24; + ice_sbox[0][i] = ice_perm32 (x); + + x = gf_exp7 (col ^ ice_sxor[1][row], ice_smod[1][row]) << 16; + ice_sbox[1][i] = ice_perm32 (x); + + x = gf_exp7 (col ^ ice_sxor[2][row], ice_smod[2][row]) << 8; + ice_sbox[2][i] = ice_perm32 (x); + + x = gf_exp7 (col ^ ice_sxor[3][row], ice_smod[3][row]); + ice_sbox[3][i] = ice_perm32 (x); + } +} + + +/* + * Create a new ICE key. + */ + +IceKey::IceKey (int n) +{ + if (!ice_sboxes_initialised) { + ice_sboxes_init (); + ice_sboxes_initialised = 1; + } + + if (n < 1) { + _size = 1; + _rounds = 8; + } else { + _size = n; + _rounds = n * 16; + } + + _keysched = new IceSubkey[_rounds]; +} + + +/* + * Destroy an ICE key. + */ + +IceKey::~IceKey () +{ + int i, j; + + for (i=0; i<_rounds; i++) + for (j=0; j<3; j++) + _keysched[i].val[j] = 0; + + _rounds = _size = 0; + + delete[] _keysched; +} + + +/* + * The single round ICE f function. + */ + +static unsigned long +ice_f ( + unsigned long p, + const IceSubkey *sk +) { + unsigned long tl, tr; /* Expanded 40-bit values */ + unsigned long al, ar; /* Salted expanded 40-bit values */ + + /* Left half expansion */ + tl = ((p >> 16) & 0x3ff) | (((p >> 14) | (p << 18)) & 0xffc00); + + /* Right half expansion */ + tr = (p & 0x3ff) | ((p << 2) & 0xffc00); + + /* Perform the salt permutation */ + // al = (tr & sk->val[2]) | (tl & ~sk->val[2]); + // ar = (tl & sk->val[2]) | (tr & ~sk->val[2]); + al = sk->val[2] & (tl ^ tr); + ar = al ^ tr; + al ^= tl; + + al ^= sk->val[0]; /* XOR with the subkey */ + ar ^= sk->val[1]; + + /* S-box lookup and permutation */ + return (ice_sbox[0][al >> 10] | ice_sbox[1][al & 0x3ff] + | ice_sbox[2][ar >> 10] | ice_sbox[3][ar & 0x3ff]); +} + + +/* + * Encrypt a block of 8 bytes of data with the given ICE key. + */ + +void +IceKey::encrypt ( + const unsigned char *ptext, + unsigned char *ctext +) const +{ + int i; + unsigned long l, r; + + l = (((unsigned long) ptext[0]) << 24) + | (((unsigned long) ptext[1]) << 16) + | (((unsigned long) ptext[2]) << 8) | ptext[3]; + r = (((unsigned long) ptext[4]) << 24) + | (((unsigned long) ptext[5]) << 16) + | (((unsigned long) ptext[6]) << 8) | ptext[7]; + + for (i = 0; i < _rounds; i += 2) { + l ^= ice_f (r, &_keysched[i]); + r ^= ice_f (l, &_keysched[i + 1]); + } + + for (i = 0; i < 4; i++) { + ctext[3 - i] = r & 0xff; + ctext[7 - i] = l & 0xff; + + r >>= 8; + l >>= 8; + } +} + + +/* + * Decrypt a block of 8 bytes of data with the given ICE key. + */ + +void +IceKey::decrypt ( + const unsigned char *ctext, + unsigned char *ptext +) const +{ + int i; + unsigned long l, r; + + l = (((unsigned long) ctext[0]) << 24) + | (((unsigned long) ctext[1]) << 16) + | (((unsigned long) ctext[2]) << 8) | ctext[3]; + r = (((unsigned long) ctext[4]) << 24) + | (((unsigned long) ctext[5]) << 16) + | (((unsigned long) ctext[6]) << 8) | ctext[7]; + + for (i = _rounds - 1; i > 0; i -= 2) { + l ^= ice_f (r, &_keysched[i]); + r ^= ice_f (l, &_keysched[i - 1]); + } + + for (i = 0; i < 4; i++) { + ptext[3 - i] = r & 0xff; + ptext[7 - i] = l & 0xff; + + r >>= 8; + l >>= 8; + } +} + + +/* + * Set 8 rounds [n, n+7] of the key schedule of an ICE key. + */ + +void +IceKey::scheduleBuild ( + unsigned short *kb, + int n, + const int *keyrot +) { + int i; + + for (i=0; i<8; i++) { + int j; + int kr = keyrot[i]; + IceSubkey *isk = &_keysched[n + i]; + + for (j=0; j<3; j++) + isk->val[j] = 0; + + for (j=0; j<15; j++) { + int k; + unsigned long *curr_sk = &isk->val[j % 3]; + + for (k=0; k<4; k++) { + unsigned short *curr_kb = &kb[(kr + k) & 3]; + int bit = *curr_kb & 1; + + *curr_sk = (*curr_sk << 1) | bit; + *curr_kb = (*curr_kb >> 1) | ((bit ^ 1) << 15); + } + } + } +} + + +/* + * Set the key schedule of an ICE key. + */ + +void +IceKey::set ( + const unsigned char *key +) { + int i; + + if (_rounds == 8) { + unsigned short kb[4]; + + for (i=0; i<4; i++) + kb[3 - i] = (key[i*2] << 8) | key[i*2 + 1]; + + scheduleBuild (kb, 0, ice_keyrot); + return; + } + + for (i=0; i<_size; i++) { + int j; + unsigned short kb[4]; + + for (j=0; j<4; j++) + kb[3 - j] = (key[i*8 + j*2] << 8) | key[i*8 + j*2 + 1]; + + scheduleBuild (kb, i*8, ice_keyrot); + scheduleBuild (kb, _rounds - 8 - i*8, &ice_keyrot[8]); + } +} + + +/* + * Return the key size, in bytes. + */ + +int +IceKey::keySize () const +{ + return (_size * 8); +} + + +/* + * Return the block size, in bytes. + */ + +int +IceKey::blockSize () const +{ + return (8); +} + +#endif // !_STATIC_LINKED || _SHARED_LIB diff --git a/r5dev/mathlib/adler32.cpp b/r5dev/mathlib/adler32.cpp new file mode 100644 index 00000000..82aac397 --- /dev/null +++ b/r5dev/mathlib/adler32.cpp @@ -0,0 +1,47 @@ +#include "core/stdafx.h" +#include "mathlib/adler32.h" + +// Mark Adler's compact Adler32 hashing algorithm +// Originally from the public domain stb.h header. +uint32_t adler32::update(uint32_t adler, const void* ptr, size_t buf_len) +{ + if (!ptr) + { + return NULL; + } + + const uint8_t* buffer = static_cast(ptr); + + const unsigned long ADLER_MOD = 65521; + unsigned long s1 = adler & 0xffff, s2 = adler >> 16; + size_t blocklen; + unsigned long i; + + blocklen = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < blocklen; i += 8) + { + s1 += buffer[0], s2 += s1; + s1 += buffer[1], s2 += s1; + s1 += buffer[2], s2 += s1; + s1 += buffer[3], s2 += s1; + s1 += buffer[4], s2 += s1; + s1 += buffer[5], s2 += s1; + s1 += buffer[6], s2 += s1; + s1 += buffer[7], s2 += s1; + + buffer += 8; + } + + for (; i < blocklen; ++i) + { + s1 += *buffer++, s2 += s1; + } + + s1 %= ADLER_MOD, s2 %= ADLER_MOD; + buf_len -= blocklen; + blocklen = 5552; + } + return (s2 << 16) + s1; +} diff --git a/r5dev/mathlib/adler32.h b/r5dev/mathlib/adler32.h new file mode 100644 index 00000000..71556647 --- /dev/null +++ b/r5dev/mathlib/adler32.h @@ -0,0 +1,7 @@ +#pragma once + +class adler32 +{ +public: + static uint32_t update(uint32_t adler, const void* ptr, size_t buf_len); +}; diff --git a/r5dev/mathlib/crc32.cpp b/r5dev/mathlib/crc32.cpp new file mode 100644 index 00000000..63cf181f --- /dev/null +++ b/r5dev/mathlib/crc32.cpp @@ -0,0 +1,22 @@ +#include "core/stdafx.h" +#include "mathlib/crc32.h" + +// Karl Malbrain's compact CRC-32, with pre and post conditioning. +// See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": +// http://www.geocities.com/malbrain/ +uint32_t crc32::update(uint32_t crc, const uint8_t* ptr, size_t buf_len) +{ + if (!ptr) + { + return NULL; + } + + crc = ~crc; + while (buf_len--) + { + uint8_t b = *ptr++; + crc = (crc >> 4) ^ s_crc32[(crc & 0xF) ^ (b & 0xF)]; + crc = (crc >> 4) ^ s_crc32[(crc & 0xF) ^ (b >> 4)]; + } + return ~crc; +} diff --git a/r5dev/mathlib/crc32.h b/r5dev/mathlib/crc32.h new file mode 100644 index 00000000..1466ef9c --- /dev/null +++ b/r5dev/mathlib/crc32.h @@ -0,0 +1,16 @@ +#pragma once +#include + +namespace +{ +} +class crc32 +{ + static inline uint32_t s_crc32[16] = + { + 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c + }; +public: + static uint32_t update(uint32_t crc, const uint8_t* ptr, size_t buf_len); +}; \ No newline at end of file diff --git a/r5dev/mathlib/parallel_for.h b/r5dev/mathlib/parallel_for.h new file mode 100644 index 00000000..ac766efd --- /dev/null +++ b/r5dev/mathlib/parallel_for.h @@ -0,0 +1,57 @@ +#include +#include +#include +#include + +/// @param[in] nb_elements : size of your for loop +/// @param[in] functor(start, end) : +/// your function processing a sub chunk of the for loop. +/// "start" is the first index to process (included) until the index "end" +/// (excluded) +/// @code +/// for(int i = start; i < end; ++i) +/// computation(i); +/// @endcode +/// @param use_threads : enable / disable threads. +/// +/// +static +void parallel_for(unsigned nb_elements, + std::function functor, + bool use_threads = true) +{ + // ------- + unsigned nb_threads_hint = std::thread::hardware_concurrency(); + unsigned nb_threads = nb_threads_hint == 0 ? 8 : (nb_threads_hint); + + unsigned batch_size = nb_elements / nb_threads; + unsigned batch_remainder = nb_elements % nb_threads; + + std::vector< std::thread > my_threads(nb_threads); + + if( use_threads ) + { + // Multithread execution + for(unsigned i = 0; i < nb_threads; ++i) + { + int start = i * batch_size; + my_threads[i] = std::thread(functor, start, start+batch_size); + } + } + else + { + // Single thread execution (for easy debugging) + for(unsigned i = 0; i < nb_threads; ++i){ + int start = i * batch_size; + functor( start, start+batch_size ); + } + } + + // Deform the elements left + int start = nb_threads * batch_size; + functor( start, start+batch_remainder); + + // Wait for the other thread to finish their task + if( use_threads ) + std::for_each(my_threads.begin(), my_threads.end(), std::mem_fn(&std::thread::join)); +} diff --git a/r5dev/mathlib/vector.h b/r5dev/mathlib/vector.h new file mode 100644 index 00000000..235b7288 --- /dev/null +++ b/r5dev/mathlib/vector.h @@ -0,0 +1,26 @@ +#pragma once + +typedef float vec_t; +typedef float vec3_t[3]; + +/*----------------------------------------------------------------------------- + * _vector.h + *-----------------------------------------------------------------------------*/ + +class Vector3 // TODO [ AMOS ]: Reverse class +{ +public: + // Members + vec_t x, // 0x0000 + y, // 0x0004 + z; // 0x0008 +}; + +class QAngle // TODO [ AMOS ]: Reverse class +{ +public: + // Members + vec_t x, // 0x0000 + y, // 0x0004 + z; // 0x0008 +}; diff --git a/r5dev/networksystem/r5net.cpp b/r5dev/networksystem/r5net.cpp new file mode 100644 index 00000000..4f814233 --- /dev/null +++ b/r5dev/networksystem/r5net.cpp @@ -0,0 +1,328 @@ +// r5net.cpp : Defines the functions for the static library. +// + +#include "core/stdafx.h" +#include "tier0/basetypes.h" +#include "tier0/cvar.h" +#include "engine/sys_utils.h" +#include "networksystem/r5net.h" + +//----------------------------------------------------------------------------- +// Purpose: returns the sdk version string. +//----------------------------------------------------------------------------- +std::string R5Net::Client::GetSDKVersion() +{ + return SDK_VERSION; +} + +//----------------------------------------------------------------------------- +// Purpose: returns a vector of hosted servers. +//----------------------------------------------------------------------------- +std::vector R5Net::Client::GetServersList(std::string& svOutMessage) +{ + std::vector vslList{}; + + nlohmann::json jsReqBody = nlohmann::json::object(); + jsReqBody["version"] = GetSDKVersion(); + + std::string reqBodyStr = jsReqBody.dump(); + + if (r5net_show_debug->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "Sending GetServerList post.\n"); + } + + httplib::Result htResults = m_HttpClient.Post("/servers", jsReqBody.dump().c_str(), jsReqBody.dump().length(), "application/json"); + + if (r5net_show_debug->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "GetServerList replied with '%d'.\n", htResults->status); + } + + if (htResults && htResults->status == 200) // STATUS_OK + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResults->body); + if (jsResultBody["success"].is_boolean() && jsResultBody["success"].get()) + { + for (auto &obj : jsResultBody["servers"]) + { + vslList.push_back( + ServerListing{ + obj.value("name",""), + obj.value("map", ""), + obj.value("ip", ""), + obj.value("port", ""), + obj.value("gamemode", ""), + obj.value("hidden", "false") == "true", + obj.value("remote_checksum", ""), + obj.value("version", GetSDKVersion()), + obj.value("encKey", "") + } + ); + } + } + else + { + if (jsResultBody["err"].is_string()) + { + svOutMessage = jsResultBody["err"].get(); + } + else + { + svOutMessage = "An unknown error occured!"; + } + } + } + else + { + if (htResults) + { + if (!htResults->body.empty()) + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResults->body); + + if (jsResultBody["err"].is_string()) + { + svOutMessage = jsResultBody["err"].get(); + } + else + { + svOutMessage = std::string("Failed to reach comp-server ") + std::to_string(htResults->status); + } + + return vslList; + } + + svOutMessage = std::string("Failed to reach comp-server ") + std::to_string(htResults->status); + return vslList; + } + + svOutMessage = "Failed to reach comp-server. Unknown error code."; + return vslList; + } + + return vslList; +} + +//----------------------------------------------------------------------------- +// Purpose: Sends host server POST request. +// Input : &svOutMessage - +// &svOutToken - +// &slServerListing - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool R5Net::Client::PostServerHost(std::string& svOutMessage, std::string& svOutToken, const ServerListing& slServerListing) +{ + nlohmann::json jsRequestBody = nlohmann::json::object(); + jsRequestBody["name"] = slServerListing.svServerName; + jsRequestBody["map"] = slServerListing.svMapName; + jsRequestBody["port"] = slServerListing.svPort; + jsRequestBody["remote_checksum"] = slServerListing.svRemoteChecksum; + jsRequestBody["version"] = GetSDKVersion(); + jsRequestBody["gamemode"] = slServerListing.svPlaylist; + jsRequestBody["encKey"] = slServerListing.svEncryptionKey; + jsRequestBody["hidden"] = slServerListing.bHidden; + + std::string svRequestBody = jsRequestBody.dump(); + + if (r5net_show_debug->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "Sending PostServerHost post '%s'.\n", svRequestBody.c_str()); + } + + httplib::Result htResults = m_HttpClient.Post("/servers/add", svRequestBody.c_str(), svRequestBody.length(), "application/json"); + + if (r5net_show_debug->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "PostServerHost replied with '%d'.\n", htResults->status); + } + + if (htResults && htResults->status == 200) // STATUS_OK + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResults->body); + if (jsResultBody["success"].is_boolean() && jsResultBody["success"].get()) + { + if (jsResultBody["token"].is_string()) + { + svOutToken = jsResultBody["token"].get(); + } + else + { + svOutToken = std::string(); + } + + return true; + } + else + { + if (jsResultBody["err"].is_string()) + { + svOutMessage = jsResultBody["err"].get(); + } + else + { + svOutMessage = "An unknown error occured!"; + } + return false; + } + } + else + { + if (htResults) + { + if (!htResults->body.empty()) + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResults->body); + + if (jsResultBody["err"].is_string()) + { + svOutMessage = jsResultBody["err"].get(); + } + else + { + svOutMessage = std::string("Failed to reach comp-server ") + std::to_string(htResults->status); + } + + svOutToken = std::string(); + return false; + } + + svOutToken = std::string(); + svOutMessage = std::string("Failed to reach comp-server ") + std::to_string(htResults->status); + return false; + } + + svOutToken = std::string(); + svOutMessage = "Failed to reach comp-server. Unknown error code."; + return false; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the server by token string. +// Input : &slOutServer - +// &svOutMessage - +// svToken - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool R5Net::Client::GetServerByToken(ServerListing& slOutServer, std::string& svOutMessage, const std::string svToken) +{ + nlohmann::json jsRequestBody = nlohmann::json::object(); + + jsRequestBody["token"] = svToken; + + if (r5net_show_debug->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "Sending GetServerByToken post.\n"); + } + + httplib::Result htResults = m_HttpClient.Post("/server/byToken", jsRequestBody.dump().c_str(), jsRequestBody.dump().length(), "application/json"); + + if (r5net_show_debug->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "GetServerByToken replied with '%d'\n", htResults->status); + } + + if (htResults && htResults->status == 200) // STATUS_OK + { + if (!htResults->body.empty()) + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResults->body); + + if (htResults && jsResultBody["success"].is_boolean() && jsResultBody["success"]) + { + slOutServer = ServerListing{ + jsResultBody["server"].value("name",""), + jsResultBody["server"].value("map", ""), + jsResultBody["server"].value("ip", ""), + jsResultBody["server"].value("port", ""), + jsResultBody["server"].value("gamemode", ""), + jsResultBody["server"].value("hidden", "false") == "true", + jsResultBody["server"].value("remote_checksum", ""), + jsResultBody["server"].value("version", GetSDKVersion()), + jsResultBody["server"].value("encKey", "") + }; + return true; + } + else + { + if (jsResultBody["err"].is_string()) + { + svOutMessage = jsResultBody["err"].get(); + } + else + { + svOutMessage = ""; + } + + slOutServer = ServerListing{}; + return false; + } + } + } + else + { + if (htResults) + { + if (!htResults->body.empty()) + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResults->body); + + if (jsResultBody["err"].is_string()) + { + svOutMessage = jsResultBody["err"].get(); + } + else + { + svOutMessage = std::string("Failed to reach comp-server ") + std::to_string(htResults->status); + } + + return false; + } + + svOutMessage = std::string("Failed to reach comp-server ") + std::to_string(htResults->status); + return false; + } + + svOutMessage = "Failed to reach comp-server. Unknown error code."; + slOutServer = ServerListing{}; + return false; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Checks if client is banned on the comp server. +// Input : svIpAddress - +// nOriginID - +// &svOutErrCl - +// Output : Returns true if banned, false if not banned. +//----------------------------------------------------------------------------- +bool R5Net::Client::GetClientIsBanned(const std::string svIpAddress, std::int64_t nOriginID, std::string& svOutErrCl) +{ + nlohmann::json jsRequestBody = nlohmann::json::object(); + jsRequestBody["ip"] = svIpAddress; + jsRequestBody["orid"] = nOriginID; + + httplib::Result htResults = m_HttpClient.Post("/banlist/isBanned", jsRequestBody.dump().c_str(), jsRequestBody.dump().length(), "application/json"); + + if (htResults && htResults->status == 200) + { + nlohmann::json jsResultBody = nlohmann::json::parse(htResults->body); + + if (jsResultBody["success"].is_boolean() && jsResultBody["success"].get()) + { + if (jsResultBody["isBanned"].is_boolean() && jsResultBody["isBanned"].get()) + { + svOutErrCl = jsResultBody.value("errCl", "Generic error (code:gen). Contact R5Reloaded developers."); + return true; + } + } + } + return false; +} +/////////////////////////////////////////////////////////////////////////////// +R5Net::Client* g_pR5net(new R5Net::Client("r5a-comp-sv.herokuapp.com")); diff --git a/r5dev/networksystem/r5net.h b/r5dev/networksystem/r5net.h new file mode 100644 index 00000000..072412a2 --- /dev/null +++ b/r5dev/networksystem/r5net.h @@ -0,0 +1,27 @@ +#pragma once +#include "serverlisting.h" + +namespace R5Net +{ + class Client + { + public: + Client(std::string serverString) : m_HttpClient(serverString.c_str()) + { + m_HttpClient.set_connection_timeout(10); + } + + std::vector GetServersList(std::string& svOutMessage); + bool PostServerHost(std::string& svOutMessage, std::string& svOutToken, const ServerListing& slServerListing); + bool GetServerByToken(ServerListing& slOutServer, std::string& svOutMessage, const std::string svToken); + bool GetClientIsBanned(std::string svIpAddress, std::int64_t nOriginID, std::string& svOutErrCl); + std::string GetSDKVersion(); + + Client* pR5net = nullptr; + Client* GetR5Net() { return pR5net; } + + private: + httplib::Client m_HttpClient; + }; +} +extern R5Net::Client* g_pR5net; diff --git a/r5dev/networksystem/serverlisting.h b/r5dev/networksystem/serverlisting.h new file mode 100644 index 00000000..c0d1d760 --- /dev/null +++ b/r5dev/networksystem/serverlisting.h @@ -0,0 +1,14 @@ +#pragma once + +struct ServerListing +{ + std::string svServerName; + std::string svMapName = "mp_rr_canyonlands_staging"; + std::string svIpAddress; + std::string svPort; + std::string svPlaylist = "survival_dev"; + bool bHidden{}; + std::string svRemoteChecksum; + std::string svVersion; + std::string svEncryptionKey; +}; diff --git a/r5dev/networksystem/sm_protocol.h b/r5dev/networksystem/sm_protocol.h new file mode 100644 index 00000000..370c50fe --- /dev/null +++ b/r5dev/networksystem/sm_protocol.h @@ -0,0 +1,39 @@ +#pragma once +//============================================================================= +enum +{ + CONNECTIONLESS_HEADER = 0xffffffff, +}; + +//============================================================================= +// Connectionless messages +enum +{ + c2s_connect = 1, + + c2s_num_messages +}; + +enum +{ + s2c_connect_accept = 1, + s2c_connect_reject, + + s2c_num_messages +}; + +//============================================================================= +enum NetworkMessageGroup_t +{ + net_group_networksystem = 0 +}; + +//============================================================================= +// Networksystem internal messages for use during valid connection +enum SystemNetworkMessageType_t +{ + net_nop = 0, // nop command used for padding + net_disconnect = 1, // disconnect (last message in connection) + + net_num_messages +}; diff --git a/r5dev/public/bansystem.cpp b/r5dev/public/bansystem.cpp new file mode 100644 index 00000000..6460679d --- /dev/null +++ b/r5dev/public/bansystem.cpp @@ -0,0 +1,193 @@ +#include "core/stdafx.h" +#include "public/include/bansystem.h" + +CBanSystem::CBanSystem() +{ + Load(); +} + +void CBanSystem::operator[](std::pair pair) +{ + AddEntry(pair.first, pair.second); +} + +//----------------------------------------------------------------------------- +// Purpose: loads and parses the banlist +//----------------------------------------------------------------------------- +void CBanSystem::Load() +{ + std::filesystem::path path = std::filesystem::current_path() /= "platform\\cfg\\banlist.cfg"; // Get current path + banlist.config + + nlohmann::json jsIn; + std::ifstream banFile(path, std::ios::in); // Parse ban list. + + int nTotalBans = 0; + + if (banFile.good() && banFile) // Check if it parsed. + { + banFile >> jsIn; // Into json. + banFile.close(); // Close file. + + if (!jsIn.is_null()) // Check if json is valid + { + if (!jsIn["totalBans"].is_null()) // Is the totalBans field populated? + { + nTotalBans = jsIn["totalBans"].get(); // Get the totalBans field. + } + } + + for (int i = 0; i < nTotalBans; i++) // Loop through total bans. + { + nlohmann::json jsEntry = jsIn[std::to_string(i).c_str()]; // Get Entry for current ban. + if (jsEntry.is_null()) // Check if entry is valid. + { + continue; + } + + + std::int64_t nOriginID = jsEntry["originID"].get(); // Get originID field from entry. + std::string svIpAddress = jsEntry["ipAddress"].get(); // Get ipAddress field from entry. + + vsvBanList.push_back(std::make_pair(svIpAddress, nOriginID)); // Push back into vector. + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: saves the banlist +//----------------------------------------------------------------------------- +void CBanSystem::Save() +{ + nlohmann::json jsOut; + + for (int i = 0; i < vsvBanList.size(); i++) + { + jsOut["totalBans"] = vsvBanList.size(); // Populate totalBans field. + jsOut[std::to_string(i).c_str()]["ipAddress"] = vsvBanList[i].first; // Populate ipAddress field for this entry. + jsOut[std::to_string(i).c_str()]["originID"] = vsvBanList[i].second; // Populate originID field for this entry. + } + + std::filesystem::path path = std::filesystem::current_path() /= "platform\\cfg\\banlist.cfg"; // Get current path + banlist.config + std::ofstream outFile(path, std::ios::out | std::ios::trunc); // Write config file.. + + outFile << jsOut.dump(4); // Dump it into config file.. + outFile.close(); // Close the file handle. +} + +//----------------------------------------------------------------------------- +// Purpose: adds a banned player entry to the banlist +//----------------------------------------------------------------------------- +void CBanSystem::AddEntry(std::string svIpAddress, std::int64_t nOriginID) +{ + if (!svIpAddress.empty() && nOriginID > 0) // Check if args are valid. + { + vsvBanList.push_back(std::make_pair(svIpAddress, nOriginID)); // Push it back into the vector. + } +} + +//----------------------------------------------------------------------------- +// Purpose: deletes an entry in the banlist +//----------------------------------------------------------------------------- +void CBanSystem::DeleteEntry(std::string svIpAddress, std::int64_t nOriginID) +{ + for (int i = 0; i < vsvBanList.size(); i++) // Loop through vector. + { + if (svIpAddress.compare(vsvBanList[i].first) == NULL || nOriginID == vsvBanList[i].second) // Do any entries match our vector? + { + vsvBanList.erase(vsvBanList.begin() + i); // If so erase that vector element. + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: adds a connect refuse entry to the refuselist +//----------------------------------------------------------------------------- +void CBanSystem::AddConnectionRefuse(std::string svError, int nUserID) +{ + if (vsvrefuseList.empty()) + { + vsvrefuseList.push_back(std::make_pair(svError, nUserID)); + } + else + { + for (int i = 0; i < vsvrefuseList.size(); i++) // Loop through vector. + { + if (vsvrefuseList[i].second != nUserID) // Do any entries match our vector? + { + vsvrefuseList.push_back(std::make_pair(svError, nUserID)); // Push it back into the vector. + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: deletes an entry in the refuselist +//----------------------------------------------------------------------------- +void CBanSystem::DeleteConnectionRefuse(int nUserID) +{ + for (int i = 0; i < vsvrefuseList.size(); i++) // Loop through vector. + { + if (vsvrefuseList[i].second == nUserID) // Do any entries match our vector? + { + vsvrefuseList.erase(vsvrefuseList.begin() + i); // If so erase that vector element. + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: checks if specified ip address or necleus id is banned +// Input : svIpAddress - +// nOriginID - +// Output : true if banned, false if not banned +//----------------------------------------------------------------------------- +bool CBanSystem::IsBanned(std::string svIpAddress, std::int64_t nOriginID) +{ + for (int i = 0; i < vsvBanList.size(); i++) + { + std::string ipAddress = vsvBanList[i].first; // Get first pair entry. + std::int64_t originID = vsvBanList[i].second; // Get second pair entry. + + if (ipAddress.empty()) // Check if ip is empty. + { + continue; + } + + + if (originID <= 0) // Is originID below 0? + { + continue; + } + + + if (ipAddress.compare(svIpAddress) == NULL) // Do they match? + { + return true; + } + + + if (nOriginID == originID) // Do they match? + { + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: checks if refuselist is valid +//----------------------------------------------------------------------------- +bool CBanSystem::IsRefuseListValid() +{ + return !vsvrefuseList.empty(); +} + +//----------------------------------------------------------------------------- +// Purpose: checks if banlist is valid +//----------------------------------------------------------------------------- +bool CBanSystem::IsBanListValid() +{ + return !vsvBanList.empty(); +} +/////////////////////////////////////////////////////////////////////////////// +CBanSystem* g_pBanSystem = new CBanSystem();; diff --git a/r5dev/public/binstream.cpp b/r5dev/public/binstream.cpp new file mode 100644 index 00000000..f376553a --- /dev/null +++ b/r5dev/public/binstream.cpp @@ -0,0 +1,171 @@ +#include "core/stdafx.h" +#include "engine/sys_utils.h" +#include "public/include/binstream.h" + +//----------------------------------------------------------------------------- +// Purpose: CIOStream constructor +//----------------------------------------------------------------------------- +CIOStream::CIOStream() +{ + eCurrentMode = eStreamFileMode::NONE; +} + +//----------------------------------------------------------------------------- +// Purpose: CIOStream destructor +//----------------------------------------------------------------------------- +CIOStream::~CIOStream() +{ + if (writer.is_open()) + { + writer.close(); + } + + if (reader.is_open()) + { + reader.close(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: opens the file in specified mode +// Input : fileFullPath - mode +// Output : true if operation is successfull +//----------------------------------------------------------------------------- +bool CIOStream::open(std::string svFileFullPath, eStreamFileMode eMode) +{ + svFilePath = svFileFullPath; + + if (eMode == eStreamFileMode::WRITE) + { + eCurrentMode = eMode; + + // check if we had a previously opened file to close it + if (writer.is_open()) + { + writer.close(); + } + + writer.open(svFilePath.c_str(), std::ios::binary); + if (!writer.is_open()) + { + DevMsg(eDLL_T::FS, "Error opening file '%s' for write operation!\n", svFilePath.c_str()); + eCurrentMode = eStreamFileMode::NONE; + } + } + // Read mode + else if (eMode == eStreamFileMode::READ) + { + eCurrentMode = eMode; + + // check if we had a previously opened file to close it + if (reader.is_open()) + { + reader.close(); + } + + reader.open(svFilePath.c_str(), std::ios::binary); + if (!reader.is_open()) + { + DevMsg(eDLL_T::FS, "Error opening file '%s' for read operation!\n", svFilePath.c_str()); + eCurrentMode = eStreamFileMode::NONE; + } + } + + // if the mode is still the NONE -> we failed + return eCurrentMode == eStreamFileMode::NONE ? false : true; +} + +//----------------------------------------------------------------------------- +// Purpose: closes the file +//----------------------------------------------------------------------------- +void CIOStream::close() +{ + if (eCurrentMode == eStreamFileMode::WRITE) + { + writer.close(); + } + else if (eCurrentMode == eStreamFileMode::READ) + { + reader.close(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: checks if we are able to read the file +//----------------------------------------------------------------------------- +bool CIOStream::checkReadabilityStatus() +{ + if (eCurrentMode != eStreamFileMode::READ) + { + DevMsg(eDLL_T::FS, "Error: StreamFileMode doesn't match required mode for read operation.\n"); + return false; + } + + // check if we hit the end of the file. + if (reader.eof()) + { + DevMsg(eDLL_T::FS, "Error: trying to read past EOF.\n"); + reader.close(); + eCurrentMode = eStreamFileMode::NONE; + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: checks if we are able to write to file +//----------------------------------------------------------------------------- +bool CIOStream::checkWritabilityStatus() +{ + if (eCurrentMode != eStreamFileMode::WRITE) + { + DevMsg(eDLL_T::FS, "Error: StreamFileMode doesn't match required mode for write operation.\n"); + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: checks if we hit the end of file +//----------------------------------------------------------------------------- +bool CIOStream::eof() +{ + return reader.eof(); +} + +//----------------------------------------------------------------------------- +// Purpose: reads a string from the file and returns it +//----------------------------------------------------------------------------- +std::string CIOStream::readString() +{ + if (checkReadabilityStatus()) + { + char c; + std::string result = ""; + while (!reader.eof() && (c = readR()) != '\0') + { + result += c; + } + + return result; + } + return ""; +} + +//----------------------------------------------------------------------------- +// Purpose: writes a string to the file +//----------------------------------------------------------------------------- +void CIOStream::writeString(std::string str) +{ + if (!checkWritabilityStatus()) + { + return; + } + + str += '\0'; // null-terminate the string. + + char* text = (char*)(str.c_str()); + size_t size = str.size(); + + writer.write((const char*)text, size); +} diff --git a/r5dev/public/include/bansystem.h b/r5dev/public/include/bansystem.h new file mode 100644 index 00000000..c293ad63 --- /dev/null +++ b/r5dev/public/include/bansystem.h @@ -0,0 +1,24 @@ +#pragma once +#include "public/include/json.hpp" + +class CBanSystem +{ +public: + CBanSystem(); + void operator[](std::pair pair); + void Load(); + void Save(); + void AddEntry(std::string svIpAddress, std::int64_t nOriginID); + void DeleteEntry(std::string svIpAddress, std::int64_t nOriginID); + void AddConnectionRefuse(std::string svError, int nUserID); + void DeleteConnectionRefuse(int nUserID); + bool IsBanned(std::string svIpAddress, std::int64_t nOriginID); + bool IsRefuseListValid(); + bool IsBanListValid(); + + std::vector> vsvrefuseList = {};; +private: + std::vector> vsvBanList = {}; +}; + +extern CBanSystem* g_pBanSystem; diff --git a/r5dev/public/include/binstream.h b/r5dev/public/include/binstream.h new file mode 100644 index 00000000..1607d97f --- /dev/null +++ b/r5dev/public/include/binstream.h @@ -0,0 +1,68 @@ +#pragma once + +enum class eStreamFileMode +{ + NONE = 0, + READ, + WRITE +}; + +class CIOStream +{ + std::ofstream writer; // Output file stream. + std::ifstream reader; // Input file stream. + std::string svFilePath = ""; // Filepath. + eStreamFileMode eCurrentMode = eStreamFileMode::NONE; // Current active mode. + +public: + CIOStream(); + ~CIOStream(); + + bool open(std::string fileFullPath, eStreamFileMode mode); + void close(); + + bool checkWritabilityStatus(); + bool checkReadabilityStatus(); + + bool eof(); + + //----------------------------------------------------------------------------- + // Purpose: reads any value from the file (for strings use 'readString(...)' instead) + //----------------------------------------------------------------------------- + template + void read(T& value) // Template functions have to be in the header! + { + if (checkReadabilityStatus()) + { + reader.read((char*)&value, sizeof(value)); + } + } + + //----------------------------------------------------------------------------- + // Purpose: reads any value from the file and returns it (for strings use 'readString(...)' instead) + //----------------------------------------------------------------------------- + template + T readR() // Template functions have to be in the header! + { + checkReadabilityStatus(); + + T value; + reader.read((char*)&value, sizeof(value)); + return value; + } + std::string readString(); + + //----------------------------------------------------------------------------- + // Purpose: writes any value to the file (for strings use 'writeString(...)' instead) + //----------------------------------------------------------------------------- + template + void write(T& value) // Template functions have to be in the header! + { + if (!checkWritabilityStatus()) + { + return; + } + writer.write((const char*)&value, sizeof(value)); + } + void writeString(std::string str); +}; diff --git a/shared/include/httplib.h b/r5dev/public/include/httplib.h similarity index 100% rename from shared/include/httplib.h rename to r5dev/public/include/httplib.h diff --git a/shared/include/json.hpp b/r5dev/public/include/json.hpp similarity index 100% rename from shared/include/json.hpp rename to r5dev/public/include/json.hpp diff --git a/shared/include/address.h b/r5dev/public/include/memaddr.h similarity index 76% rename from shared/include/address.h rename to r5dev/public/include/memaddr.h index b3cc6c22..0d7cf4b4 100644 --- a/shared/include/address.h +++ b/r5dev/public/include/memaddr.h @@ -1,6 +1,7 @@ #pragma once +#include "public/include/utility.h" -class MemoryAddress +class ADDRESS { public: @@ -15,9 +16,9 @@ public: return ptr; } - MemoryAddress() = default; - MemoryAddress(std::uintptr_t ptr) : ptr(ptr) {} - MemoryAddress(void* ptr) : ptr(std::uintptr_t(ptr)) {} + ADDRESS() = default; + ADDRESS(std::uintptr_t ptr) : ptr(ptr) {} + ADDRESS(void* ptr) : ptr(std::uintptr_t(ptr)) {} operator std::uintptr_t() const { @@ -34,12 +35,12 @@ public: return ptr != NULL; } - bool operator!= (const MemoryAddress& addr) const + bool operator!= (const ADDRESS& addr) const { return ptr != addr.ptr; } - bool operator== (const MemoryAddress& addr) const + bool operator== (const ADDRESS& addr) const { return ptr == addr.ptr; } @@ -64,18 +65,18 @@ public: return *reinterpret_cast(ptr); } - MemoryAddress Offset(std::ptrdiff_t offset) + ADDRESS Offset(std::ptrdiff_t offset) { - return MemoryAddress(ptr + offset); + return ADDRESS(ptr + offset); } - MemoryAddress OffsetSelf(std::ptrdiff_t offset) + ADDRESS OffsetSelf(std::ptrdiff_t offset) { ptr += offset; return *this; } - MemoryAddress Deref(int deref = 1) + ADDRESS Deref(int deref = 1) { std::uintptr_t reference = ptr; @@ -85,10 +86,10 @@ public: reference = *reinterpret_cast(reference); } - return MemoryAddress(reference); + return ADDRESS(reference); } - MemoryAddress DerefSelf(int deref = 1) + ADDRESS DerefSelf(int deref = 1) { while (deref--) { @@ -105,7 +106,7 @@ public: for (auto [byteAtCurrentAddress, i] = std::tuple{ std::uint8_t(), (std::size_t)0 }; i < opcodeArray.size(); i++, reference++) // Loop forward in the ptr class member. { - byteAtCurrentAddress = *reinterpret_cast(reference); // Get byte at current address. + byteAtCurrentAddress = *reinterpret_cast(reference); // Get byte at current Address. if (byteAtCurrentAddress != opcodeArray[i]) // If byte at ptr doesn't equal in the byte array return false. return false; @@ -128,14 +129,14 @@ public: for (int i = 0; i < opcodes.size(); i++) { - *(std::uint8_t*)(ptr + i) = opcodes[i]; // Write opcodes to address. + *(std::uint8_t*)(ptr + i) = opcodes[i]; // Write opcodes to Address. } dwSize = opcodes.size(); VirtualProtect((void*)ptr, dwSize, oldProt, &oldProt); // Restore protection. } - MemoryAddress FindPatternSelf(const std::string pattern, const Direction searchDirect, const int opCodesToScan = 100, const std::ptrdiff_t occurence = 1) + ADDRESS FindPatternSelf(const std::string pattern, const Direction searchDirect, const int opCodesToScan = 100, const std::ptrdiff_t occurence = 1) { static auto PatternToBytes = [](const std::string pattern) { @@ -206,7 +207,7 @@ public: return *this; } - MemoryAddress FindPattern(const std::string pattern, const Direction searchDirect, const int opCodesToScan = 100, const std::ptrdiff_t occurence = 1) + ADDRESS FindPattern(const std::string pattern, const Direction searchDirect, const int opCodesToScan = 100, const std::ptrdiff_t occurence = 1) { static auto PatternToBytes = [](const std::string pattern) { @@ -266,60 +267,60 @@ public: occurences++; if (occurence == occurences) { - return MemoryAddress(&*(ScanBytes + memOffset)); + return ADDRESS(&*(ScanBytes + memOffset)); } } } - return MemoryAddress(); + return ADDRESS(); } - MemoryAddress FollowNearCall(std::ptrdiff_t opcodeOffset = 0x1, std::ptrdiff_t nextInstructionOffset = 0x5) + ADDRESS FollowNearCall(std::ptrdiff_t opcodeOffset = 0x1, std::ptrdiff_t nextInstructionOffset = 0x5) { return ResolveRelativeAddress(opcodeOffset, nextInstructionOffset); } - MemoryAddress FollowNearCallSelf(std::ptrdiff_t opcodeOffset = 0x1, std::ptrdiff_t nextInstructionOffset = 0x5) + ADDRESS FollowNearCallSelf(std::ptrdiff_t opcodeOffset = 0x1, std::ptrdiff_t nextInstructionOffset = 0x5) { return ResolveRelativeAddressSelf(opcodeOffset, nextInstructionOffset); } - MemoryAddress ResolveRelativeAddressSelf(std::ptrdiff_t registerOffset = 0x0, std::ptrdiff_t nextInstructionOffset = 0x4) + ADDRESS ResolveRelativeAddressSelf(std::ptrdiff_t registerOffset = 0x0, std::ptrdiff_t nextInstructionOffset = 0x4) { // Skip register. std::uintptr_t skipRegister = ptr + registerOffset; - // Get 4-byte long relative address. + // Get 4-byte long relative Address. std::int32_t relativeAddress = *reinterpret_cast(skipRegister); // Get location of next instruction. std::uintptr_t nextInstruction = ptr + nextInstructionOffset; - // Get function location via adding relative address to next instruction. + // Get function location via adding relative Address to next instruction. ptr = nextInstruction + relativeAddress; return *this; } - MemoryAddress ResolveRelativeAddress(std::ptrdiff_t registerOffset = 0x0, std::ptrdiff_t nextInstructionOffset = 0x4) + ADDRESS ResolveRelativeAddress(std::ptrdiff_t registerOffset = 0x0, std::ptrdiff_t nextInstructionOffset = 0x4) { // Skip register. std::uintptr_t skipRegister = ptr + registerOffset; - // Get 4-byte long relative address. + // Get 4-byte long relative Address. std::int32_t relativeAddress = *reinterpret_cast(skipRegister); // Get location of next instruction. std::uintptr_t nextInstruction = ptr + nextInstructionOffset; - // Get function location via adding relative address to next instruction. - return MemoryAddress(nextInstruction + relativeAddress); + // Get function location via adding relative Address to next instruction. + return ADDRESS(nextInstruction + relativeAddress); } private: std::uintptr_t ptr = 0; }; -class Module +class MODULE { public: @@ -334,7 +335,7 @@ public: } std::string sectionName = std::string(); // Name of section. - std::uintptr_t sectionStartAddress = 0; // Start memory address of section. + std::uintptr_t sectionStartAddress = 0; // Start memory Address of section. DWORD sectionSize = 0; // Size of section. }; @@ -349,8 +350,8 @@ public: return ModuleSections(); } - Module() = default; - Module(std::string moduleName) : moduleName(moduleName) + MODULE() = default; + MODULE(std::string moduleName) : moduleName(moduleName) { const MODULEINFO mInfo = GetModuleInfo(moduleName.c_str()); // Get module info. sizeOfModule = (DWORD64)mInfo.SizeOfImage; // Grab the module size. @@ -368,7 +369,7 @@ public: } } - MemoryAddress PatternSearch(const std::string pattern, const std::ptrdiff_t patternOccurence = 1) + ADDRESS PatternSearch(const std::string pattern, const std::ptrdiff_t patternOccurence = 1) { static auto PatternToBytes = [](const std::string pattern) { @@ -401,7 +402,7 @@ public: ModuleSections textSection = GetSectionByName(".text"); // Get the .text section. if (!textSection.IsSectionValid()) - return MemoryAddress(); + return ADDRESS(); const std::vector PatternBytes = PatternToBytes(pattern); // Convert our pattern to a byte array. const std::pair BytesInfo = std::make_pair(PatternBytes.size(), PatternBytes.data()); // Get the size and data of our bytes. @@ -430,62 +431,119 @@ public: { occurencesFound++; // Increment occurences found counter. if (patternOccurence == occurencesFound) // Is it the occurence we want? - return MemoryAddress(&StartOfCodeSection[i]); // If yes return it. + return ADDRESS(&StartOfCodeSection[i]); // If yes return it. latestOccurence = &StartOfCodeSection[i]; // Stash latest occurence. } } - return MemoryAddress(latestOccurence); + return ADDRESS(latestOccurence); } - MemoryAddress GetExportedFunction(const std::string functionName) + ADDRESS FindPatternSIMD(std::uint8_t* szPattern, const char* szMask) + { + + ModuleSections mInfo = GetSectionByName(".text"); // Get the .text section. + if (!mInfo.IsSectionValid()) { return ADDRESS(); } + + DWORD64 base = (DWORD64)mInfo.sectionStartAddress; + DWORD64 size = (DWORD64)mInfo.sectionSize; + + unsigned char* pData = (unsigned char*)base; + unsigned int length = (unsigned int)size; + + const unsigned char* end = pData + length - strlen(szMask); + int num_masks = (int)ceil((float)strlen(szMask) / (float)16); + int masks[32]; // 32*16 = enough masks for 512 bytes. + memset(masks, 0, num_masks * sizeof(int)); + for (int64_t i = 0; i < num_masks; ++i) + { + for (int64_t j = strnlen(szMask + i * 16, 16) - 1; j >= 0; --j) + { + if (szMask[i * 16 + j] == 'x') + { + masks[i] |= 1 << j; + } + } + } + __m128i xmm1 = _mm_loadu_si128((const __m128i*) szPattern); + __m128i xmm2, xmm3, msks; + for (; pData != end; _mm_prefetch((const char*)(++pData + 64), _MM_HINT_NTA)) + { + if (szPattern[0] == pData[0]) + { + xmm2 = _mm_loadu_si128((const __m128i*) pData); + msks = _mm_cmpeq_epi8(xmm1, xmm2); + if ((_mm_movemask_epi8(msks) & masks[0]) == masks[0]) + { + for (DWORD64 i = 1; i < num_masks; ++i) + { + xmm2 = _mm_loadu_si128((const __m128i*) (pData + i * 16)); + xmm3 = _mm_loadu_si128((const __m128i*) (szPattern + i * 16)); + msks = _mm_cmpeq_epi8(xmm2, xmm3); + if ((_mm_movemask_epi8(msks) & masks[i]) == masks[i]) + { + if ((i + 1) == num_masks) + { + return (ADDRESS)pData; + } + } + else goto cont; + } + return (ADDRESS)(&*(pData)); + } + }cont:; + } + return ADDRESS(); + } + + ADDRESS GetExportedFunction(const std::string functionName) { if (!dosHeader || dosHeader->e_magic != IMAGE_DOS_SIGNATURE) // Is dosHeader valid? - return MemoryAddress(); + return ADDRESS(); if (!ntHeaders || ntHeaders->Signature != IMAGE_NT_SIGNATURE) // Is ntHeader valid? - return MemoryAddress(); + return ADDRESS(); - // Get the location of IMAGE_EXPORT_DIRECTORY for this module by adding the IMAGE_DIRECTORY_ENTRY_EXPORT relative virtual address onto our module base address. + // Get the location of IMAGE_EXPORT_DIRECTORY for this module by adding the IMAGE_DIRECTORY_ENTRY_EXPORT relative virtual Address onto our module base Address. IMAGE_EXPORT_DIRECTORY* ImageExportDirectory = reinterpret_cast(moduleBase + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); if (!ImageExportDirectory) - return MemoryAddress(); + return ADDRESS(); // Are there any exported functions? if (!ImageExportDirectory->NumberOfFunctions) - return MemoryAddress(); + return ADDRESS(); - // Get the location of the functions via adding the relative virtual address from the struct into our module base address. + // Get the location of the functions via adding the relative virtual Address from the struct into our module base Address. DWORD* AddressOfFunctionsPtr = reinterpret_cast(moduleBase + ImageExportDirectory->AddressOfFunctions); if (!AddressOfFunctionsPtr) - return MemoryAddress(); + return ADDRESS(); - // Get the names of the functions via adding the relative virtual address from the struct into our module base address. + // Get the names of the functions via adding the relative virtual Address from the struct into our module base Address. DWORD* AddressOfNamePtr = reinterpret_cast(moduleBase + ImageExportDirectory->AddressOfNames); if (!AddressOfNamePtr) - return MemoryAddress(); + return ADDRESS(); - // Get the ordinals of the functions via adding the relative virtual address from the struct into our module base address. + // Get the ordinals of the functions via adding the relative virtual Address from the struct into our module base Address. DWORD* AddressOfOrdinalsPtr = reinterpret_cast(moduleBase + ImageExportDirectory->AddressOfNameOrdinals); if (!AddressOfOrdinalsPtr) - return MemoryAddress(); + return ADDRESS(); for (std::size_t i = 0; i < ImageExportDirectory->NumberOfFunctions; i++) // Iterate through all the functions. { - // Get virtual relative address of the function name. Then add module base address to get the actual location. + // Get virtual relative Address of the function name. Then add module base Address to get the actual location. std::string ExportFunctionName = reinterpret_cast(reinterpret_cast(moduleBase + AddressOfNamePtr[i])); if (ExportFunctionName.compare(functionName) == 0) // Is this our wanted exported function? { - // Get the function ordinal. Then grab the relative virtual address of our wanted function. Then add module base address so we get the actual location. - return MemoryAddress(moduleBase + AddressOfFunctionsPtr[reinterpret_cast(AddressOfOrdinalsPtr)[i]]); // Return as MemoryAddress class. + // Get the function ordinal. Then grab the relative virtual Address of our wanted function. Then add module base Address so we get the actual location. + return ADDRESS(moduleBase + AddressOfFunctionsPtr[reinterpret_cast(AddressOfOrdinalsPtr)[i]]); // Return as Address class. } } - return MemoryAddress(); + return ADDRESS(); } - MemoryAddress FindAddressForString(const std::string string, bool nullTerminator) + ADDRESS FindAddressForString(const std::string string, bool nullTerminator) { static auto StringToBytes = [](const std::string string, bool nullTerminator) { @@ -507,7 +565,7 @@ public: ModuleSections rdataSection = GetSectionByName(".rdata"); // .Get rdata section, we only loop through here because most important strings are in the .rdata section. if (!rdataSection.IsSectionValid()) - return MemoryAddress(); + return ADDRESS(); std::vector stringBytes = StringToBytes(string, nullTerminator); // Convert our string to a byte array. const std::pair BytesInfo = std::make_pair(stringBytes.size(), stringBytes.data()); // Get the size and data of our bytes. @@ -531,14 +589,14 @@ public: if (FoundAddress) { - return MemoryAddress(&StartOfRdata[i]); + return ADDRESS(&StartOfRdata[i]); } } - return MemoryAddress(); + return ADDRESS(); } - MemoryAddress StringSearch(const std::string string, const std::ptrdiff_t occurence = 1, bool nullTerminator = false) + ADDRESS StringSearch(const std::string string, const std::ptrdiff_t occurence = 1, bool nullTerminator = false) { static auto PatternToBytes = [](const std::string pattern) { @@ -571,11 +629,11 @@ public: ModuleSections textSection = GetSectionByName(".text"); // Get the .text section. if (!textSection.IsSectionValid()) - return MemoryAddress(); + return ADDRESS(); - MemoryAddress stringAddress = FindAddressForString(string, nullTerminator); // Get address for the string in the .rdata section. + ADDRESS stringAddress = FindAddressForString(string, nullTerminator); // Get Address for the string in the .rdata section. if (!stringAddress) - return MemoryAddress(); + return ADDRESS(); std::uint8_t* latestOccurence = nullptr; std::ptrdiff_t occurencesFound = 0; @@ -587,25 +645,25 @@ public: byte byte = StartOfCodeSection[i]; if (byte == 0x8D) // is it a LEA instruction? { - MemoryAddress skipOpCode = MemoryAddress((std::uintptr_t)&StartOfCodeSection[i]).OffsetSelf(0x2); // Skip next 2 opcodes, those being the instruction and then the register. + ADDRESS skipOpCode = ADDRESS((std::uintptr_t)&StartOfCodeSection[i]).OffsetSelf(0x2); // Skip next 2 opcodes, those being the instruction and then the register. - std::int32_t relativeAddress = skipOpCode.GetValue(); // Get 4-byte long string relative address + std::int32_t relativeAddress = skipOpCode.GetValue(); // Get 4-byte long string relative Address std::uintptr_t nextInstruction = skipOpCode.Offset(0x4).GetPtr(); // Get location of next instruction. - MemoryAddress potentialLocation = MemoryAddress(nextInstruction + relativeAddress); // Get potential string location. + ADDRESS potentialLocation = ADDRESS(nextInstruction + relativeAddress); // Get potential string location. if (potentialLocation == stringAddress) { occurencesFound++; // Increment occurences found counter. if (occurence == occurencesFound) // Is it the occurence we want? - return MemoryAddress(&StartOfCodeSection[i]); // If yes return it. + return ADDRESS(&StartOfCodeSection[i]); // If yes return it. latestOccurence = &StartOfCodeSection[i]; // Stash latest occurence. } } } - return MemoryAddress(latestOccurence); + return ADDRESS(latestOccurence); } std::uintptr_t GetModuleBase() diff --git a/r5dev/public/include/stb_image.h b/r5dev/public/include/stb_image.h new file mode 100644 index 00000000..d60371b9 --- /dev/null +++ b/r5dev/public/include/stb_image.h @@ -0,0 +1,7897 @@ +/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine Simon Breuss (16-bit PNM) + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko github:mosra + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + Jacko Dirks + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_global = flag_true_if_should_convert; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind( s ); + return 0; + } + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8)); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/r5dev/public/include/utility.h b/r5dev/public/include/utility.h new file mode 100644 index 00000000..fa4ec9ee --- /dev/null +++ b/r5dev/public/include/utility.h @@ -0,0 +1,20 @@ +#pragma once +#include + +///////////////////////////////////////////////////////////////////////////// +// Internals +BOOL FileExists(LPCTSTR szPath); +MODULEINFO GetModuleInfo(const char* szModule); +DWORD64 FindPatternV1(const char* szModule, const unsigned char* szPattern, const char* szMask); +DWORD64 FindPatternSIMD(const char* szModule, const unsigned char* szPattern, const char* szMask); + +///////////////////////////////////////////////////////////////////////////// +// Utility +void DbgPrint(LPCSTR sFormat, ...); +void HexDump(const char* szHeader, int nFunc, const void* pData, int nSize); +std::string Base64Encode(const std::string& in); +std::string Base64Decode(const std::string& in); +bool StringReplace(std::string& str, const std::string& from, const std::string& to); +std::string CreateDirectories(std::string svFilePath); + +///////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/public/utility.cpp b/r5dev/public/utility.cpp new file mode 100644 index 00000000..d92099b0 --- /dev/null +++ b/r5dev/public/utility.cpp @@ -0,0 +1,298 @@ +/*----------------------------------------------------------------------------- + * _utility + *-----------------------------------------------------------------------------*/ + +#include "core/stdafx.h" +#include "core/logdef.h" +#include "public/include/utility.h" + +/////////////////////////////////////////////////////////////////////////////// +// For checking if a specific file exists. +BOOL FileExists(LPCTSTR szPath) +{ + DWORD dwAttrib = GetFileAttributes(szPath); + + return (dwAttrib != INVALID_FILE_ATTRIBUTES && + !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +} + +/////////////////////////////////////////////////////////////////////////////// +// For getting information about the executing module. +MODULEINFO GetModuleInfo(const char* szModule) +{ + MODULEINFO modinfo = { 0 }; + HMODULE hModule = GetModuleHandle(szModule); + if (hModule == 0) + { + return modinfo; + } + GetModuleInformation(GetCurrentProcess(), hModule, &modinfo, sizeof(MODULEINFO)); + return modinfo; +} + +/////////////////////////////////////////////////////////////////////////////// +// For finding a byte pattern in memory of the process. +BOOL Compare(const unsigned char* pData, const unsigned char* szPattern, const char* szMask) +{ + for (; *szMask; ++szMask, ++pData, ++szPattern) + { + if (*szMask == 'x' && *pData != *szPattern) + { + return false; + } + } + return (*szMask) == NULL; +} +DWORD64 FindPatternV1(const char* szModule, const unsigned char* szPattern, const char* szMask) +{ + MODULEINFO mInfo = GetModuleInfo(szModule); + DWORD64 dwAddress = (DWORD64)mInfo.lpBaseOfDll; + DWORD64 dwLen = (DWORD64)mInfo.SizeOfImage; + + size_t maskLen = strlen(szMask); + for (int i = 0; i < dwLen - maskLen; i++) + { + if (Compare((unsigned char*)(dwAddress + i), szPattern, szMask)) + { + return (dwAddress + i); + } + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// For finding a pattern in memory of the process with SIMD. +DWORD64 FindPatternSIMD(const char* szModule, const unsigned char* szPattern, const char* szMask) +{ + MODULEINFO mInfo = GetModuleInfo(szModule); + DWORD64 dwBase = (DWORD64)mInfo.lpBaseOfDll; + DWORD64 dwSize = (DWORD64)mInfo.SizeOfImage; + + unsigned char* pData = (unsigned char*)dwBase; + unsigned int length = (unsigned int)dwSize; + + const unsigned char* end = pData + length - strlen(szMask); + int num_masks = (int)ceil((float)strlen(szMask) / (float)16); + int masks[32]; // 32*16 = enough masks for 512 bytes. + memset(masks, 0, num_masks * sizeof(int)); + for (int64_t i = 0; i < num_masks; ++i) + { + for (int64_t j = strnlen(szMask + i * 16, 16) - 1; j >= 0; --j) + { + if (szMask[i * 16 + j] == 'x') + { + masks[i] |= 1 << j; + } + } + } + __m128i xmm1 = _mm_loadu_si128((const __m128i*) szPattern); + __m128i xmm2, xmm3, msks; + for (; pData != end; _mm_prefetch((const char*)(++pData + 64), _MM_HINT_NTA)) + { + if (szPattern[0] == pData[0]) + { + xmm2 = _mm_loadu_si128((const __m128i*) pData); + msks = _mm_cmpeq_epi8(xmm1, xmm2); + if ((_mm_movemask_epi8(msks) & masks[0]) == masks[0]) + { + for (DWORD64 i = 1; i < num_masks; ++i) + { + xmm2 = _mm_loadu_si128((const __m128i*) (pData + i * 16)); + xmm3 = _mm_loadu_si128((const __m128i*) (szPattern + i * 16)); + msks = _mm_cmpeq_epi8(xmm2, xmm3); + if ((_mm_movemask_epi8(msks) & masks[i]) == masks[i]) + { + if ((i + 1) == num_masks) + { + return (DWORD64)pData; + } + } + else goto cont; + } + return (DWORD64)pData; + } + }cont:; + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// For printing output to the debugger. +void DbgPrint(LPCSTR sFormat, ...) +{ + CHAR sBuffer[512] = { 0 }; + va_list sArgs = {}; + + // Get the variable arg pointer. + va_start(sArgs, sFormat); + + // Format print the string. + int length = vsnprintf(sBuffer, sizeof(sBuffer), sFormat, sArgs); + va_end(sArgs); + + // Output the string to the debugger. + OutputDebugString(sBuffer); +} + +/////////////////////////////////////////////////////////////////////////////// +// For dumping data from a buffer to a file on the disk +void HexDump(const char* szHeader, int nFunc, const void* pData, int nSize) +{ + static unsigned char szAscii[17] = {}; + static std::atomic i = {}, j = {}, k = {}; + static std::shared_ptr logger = spdlog::get("default_logger"); + + // Loop until the function returned to the first caller. + while (k == 1) { /*Sleep(75);*/ } + + k = 1; + szAscii[16] = '\0'; + + // Add new loggers here to replace the placeholder. + if (nFunc == 0) { logger = g_spd_netchan_logger; } + + // Add timestamp. + logger->set_level(spdlog::level::trace); + logger->set_pattern("%v [%H:%M:%S.%f]\n"); + logger->trace("---------------------------------------------------------"); + + // Disable EOL and create block header. + logger->set_pattern("%v"); + logger->trace("{:s} ---- LEN BYTES: {}\n:\n", szHeader, nSize); + logger->trace("-------- 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n"); + + // Output the buffer to the file. + for (i = 0; i < nSize; i++) + { + if (i % nSize == 0) { logger->trace(" 0x{:04X} ", i); } + logger->trace("{:02x} ", ((unsigned char*)pData)[i]); + + if (((unsigned char*)pData)[i] >= ' ' && ((unsigned char*)pData)[i] <= '~') { szAscii[i % 16] = ((unsigned char*)pData)[i]; } + else { szAscii[i % 16] = '.'; } + + if ((i + 1) % 8 == 0 || i + 1 == nSize) + { + logger->trace(" "); + + if ((i + 1) % 16 == 0) + { + if (i + 1 == nSize) + { + logger->trace("{:s}\n", szAscii); + logger->trace("---------------------------------------------------------------------------\n"); + logger->trace("\n"); + } + else + { + i++; + logger->trace("{:s}\n ", szAscii); + logger->trace("0x{:04X} ", i--); + } + } + else if (i + 1 == nSize) + { + szAscii[(i + 1) % 16] = '\0'; + if ((i + 1) % 16 <= 8) + { + logger->trace(" "); + } + for (j = (i + 1) % 16; j < 16; j++) + { + logger->trace(" "); + } + logger->trace("{:s}\n", szAscii); + logger->trace("---------------------------------------------------------------------------\n"); + logger->trace("\n"); + } + } + } + k = 0; + /////////////////////////////////////////////////////////////////////////// +} + +/////////////////////////////////////////////////////////////////////////////// +// For encoding data in base64. +std::string Base64Encode(const std::string& in) +{ + std::string results; + int val = 0, valb = -6; + + for (unsigned char c : in) + { + val = (val << 8) + c; + valb += 8; + while (valb >= 0) + { + results.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val >> valb) & 0x3F]); + valb -= 6; + } + } + if (valb > -6) + { + results.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val << 8) >> (valb + 8)) & 0x3F]); + } + while (results.size() % 4) + { + results.push_back('='); + } + return results; +} + +/////////////////////////////////////////////////////////////////////////////// +// For decoding data in base64. +std::string Base64Decode(const std::string& in) +{ + std::string results; + int val = 0, valb = -8; + + std::vector T(256, -1); + for (int i = 0; i < 64; i++) + { + T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; + } + + for (unsigned char c : in) + { + if (T[c] == -1) + { + break; + } + val = (val << 6) + T[c]; + valb += 6; + if (valb >= 0) + { + results.push_back(char((val >> valb) & 0xFF)); + valb -= 8; + } + } + return results; +} + +/////////////////////////////////////////////////////////////////////////////// +// For replacing parts of a given string. +bool StringReplace(std::string& str, const std::string& from, const std::string& to) +{ + size_t start_pos = str.find(from); + if (start_pos == std::string::npos) + { + return false; + } + + str.replace(start_pos, from.length(), to); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// For creating directories for output streams. +std::string CreateDirectories(std::string svFilePath) +{ + std::filesystem::path fspPathOut(svFilePath); + std::string results = fspPathOut.u8string(); + + StringReplace(svFilePath, "\\ \\", "\\"); + fspPathOut = fspPathOut.parent_path(); + + std::filesystem::create_directories(fspPathOut); + + return results; +} diff --git a/r5dev/r5dev.def b/r5dev/r5dev.def index fadeb105..3438df9f 100644 --- a/r5dev/r5dev.def +++ b/r5dev/r5dev.def @@ -1,4 +1,4 @@ -LIBRARY r5detours +LIBRARY r5apexsdkd EXPORTS - DummyExport @1 \ No newline at end of file + DummyExport @1 diff --git a/r5launcher/Apex.props b/r5dev/r5dev.props similarity index 100% rename from r5launcher/Apex.props rename to r5dev/r5dev.props diff --git a/r5dev/r5dev.vcxproj b/r5dev/r5dev.vcxproj index b26f9ccf..f6378d8b 100644 --- a/r5dev/r5dev.vcxproj +++ b/r5dev/r5dev.vcxproj @@ -1,4 +1,4 @@ - + @@ -18,6 +18,283 @@ x64 + + + + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + NotUsing + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 16.0 Win32Proj @@ -30,29 +307,30 @@ DynamicLibrary true - v142 + v143 Unicode DynamicLibrary false - v142 + v143 true Unicode DynamicLibrary true - v142 + v143 MultiByte + Static DynamicLibrary false - v142 + v143 true MultiByte - false + Static @@ -71,7 +349,7 @@ - + @@ -82,22 +360,21 @@ true - $(SolutionDir)r5net\include\r5;$(SolutionDir)external\minhook\include;$(SolutionDir)external\imgui\include;$(SolutionDir)external\spdlog\include;$(SolutionDir)shared\include;$(SolutionDir)r5dev\include;$(IncludePath) - $(SolutionDir)external\minhook\lib\$(Configuration);$(SolutionDir)r5net\lib\$(Configuration);$(LibraryPath) + $(SolutionDir)r5dev\;$(IncludePath);$(DXSDK_DIR)Include + $(SolutionDir)r5dev\thirdparty\detours\libs;$(SolutionDir)r5dev\thirdparty\lzham\libs;$(LibraryPath);$(DXSDK_DIR)Lib\x64 + r5apexvtxd $(SolutionDir)bin\$(Configuration)\ - r5detours $(SolutionDir)build\$(ProjectName)\$(Configuration)\ + $(VC_ReferencesPath_x64); false - $(SolutionDir)r5net\include\r5;$(SolutionDir)external\minhook\include;$(SolutionDir)external\imgui\include;$(SolutionDir)external\spdlog\include;$(SolutionDir)shared\include;$(SolutionDir)r5dev\include;$(IncludePath) - $(SolutionDir)external\minhook\lib\$(Configuration);$(SolutionDir)r5net\lib\$(Configuration);$(LibraryPath) + $(SolutionDir)r5dev\;$(IncludePath);$(DXSDK_DIR)Include + $(SolutionDir)r5dev\thirdparty\detours\libs;$(SolutionDir)r5dev\thirdparty\lzham\libs;$(LibraryPath);$(DXSDK_DIR)Lib\x64 + r5apexsdkd + $(VC_ReferencesPath_x64); $(SolutionDir)bin\$(Configuration)\ $(SolutionDir)build\$(ProjectName)\$(Configuration)\ - r5detours - - - false @@ -142,31 +419,22 @@ _DEBUG;R5DEV_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use - pch.h - stdc17 + core\stdafx.h + + stdcpp17 - MultiThreadedDebug + stdc17 Windows true false r5dev.def - Minhook.x64.lib;r5net.lib;bcrypt.lib;%(AdditionalDependencies) + detours.lib;lzhamlib_x64D.lib;lzhamcomp_x64D.lib;lzhamdecomp_x64D.lib;d3d11.lib;bcrypt.lib;%(AdditionalDependencies) - del "$(SolutionDir)bin\$(Configuration)\r5dev.dll" -rename "$(TargetPath)" "r5dev.dll" + copy /Y "$(TargetPath)" "..\..\..\bin\$(TargetFileName)" - - if not EXIST $(SolutionDir)external\minhook\lib\$(Configuration)\Minhook.x64.lib ( -"$(DevEnvDir)devenv" "$(SolutionDir)apex.sln" /Rebuild $(Configuration) /project "$(SolutionDir)external\minhook\libMinHook.vcxproj" -); - -if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib ( -"$(DevEnvDir)devenv" "$(SolutionDir)apex.sln" /Rebuild $(Configuration) /project "$(SolutionDir)r5net\r5net.vcxproj" -); - @@ -177,14 +445,19 @@ if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib ( NDEBUG;R5DEV_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true Use - pch.h + core\stdafx.h + Speed + + + true + true + AnySuitable stdc17 stdcpp17 - AnySuitable - Speed - true true - MultiThreadedDLL + + + true Windows @@ -193,249 +466,12 @@ if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib ( true false r5dev.def - Minhook.x64.lib;r5net.lib;bcrypt.lib;%(AdditionalDependencies) + detours.lib;lzhamlib_x64.lib;lzhamcomp_x64.lib;lzhamdecomp_x64.lib;d3d11.lib;bcrypt.lib;%(AdditionalDependencies) - - + copy /Y "$(TargetPath)" "..\..\..\$(TargetFileName)" && del "..\..\..\r5apexsdkd64.dll" && rename "..\..\..\$(TargetFileName)" "r5apexsdkd64.dll" - - if not EXIST $(SolutionDir)external\minhook\lib\$(Configuration)\Minhook.x64.lib ( -"$(DevEnvDir)devenv" "$(SolutionDir)apex.sln" /Rebuild $(Configuration) /project "$(SolutionDir)external\minhook\libMinHook.vcxproj" -); - -if not EXIST $(SolutionDir)r5net\lib\$(Configuration)\r5net.lib ( -"$(DevEnvDir)devenv" "$(SolutionDir)apex.sln" /Rebuild $(Configuration) /project "$(SolutionDir)r5net\r5net.vcxproj" -); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - NotUsing - NotUsing - - - - - - Use - pch.h - - - Use - pch.h - - - Use - pch.h - - - - - - - - - - - - - - - - - Use - pch.h - - - Use - pch.h - - - - - Use - pch.h - - - Use - pch.h - - - Create - pch.h - Create - pch.h - - - Use - pch.h - - - - - - - - {f04be619-0326-4ff1-b06b-fbe882e04d5e} - - diff --git a/r5dev/r5dev.vcxproj.filters b/r5dev/r5dev.vcxproj.filters index 7d03082e..d2e7e5bf 100644 --- a/r5dev/r5dev.vcxproj.filters +++ b/r5dev/r5dev.vcxproj.filters @@ -2,611 +2,843 @@ - {927ea852-3616-4fc4-8b32-781f65853a6b} + {5857edb8-da8e-491a-96a1-00a4fcfe3d38} - - {b421eb0e-1e3d-4a7a-a8d8-f9b397911702} + + {8de4910c-a79a-4e5b-8da1-d6da52425492} - - {c2ad07a8-ef40-4faa-b86c-19b31bf3209d} + + {d2a0971d-1de6-47f7-8a77-4246bc0cda3c} - - {633d6e7a-c709-4d64-a134-b383d43c8c8e} + + {c00eb6b9-50b2-4fc3-9e24-d8f7ff601e0e} - - {7cef4f92-94d9-43af-bea5-c6963d68fff2} + + {8caa20bd-7dea-4cb9-a69f-e90498e96a93} - - {c72b9789-72e9-4657-8a42-8712aaf8690e} + + {c069ad8a-c3be-4e61-afbf-f3eee3846d83} - - {757af774-e575-4623-8582-71efb0ae53a4} + + {0b1b90e6-79f6-4b19-8f5e-aac20d346094} - - {c18fb898-adc3-4aa8-902c-4777bbc76e5b} + + {fb47caac-589a-490f-9a7d-00f53add78cc} - - {7b737633-f8a6-464b-868c-c63854984082} + + {4afffd57-c78e-420f-a0a7-863b04e64812} - - {f832e462-9066-45a9-8f58-f865d5c24de3} + + {86b2ad0f-8709-48fa-8e92-0e9b8053ffce} - - {af02cb01-8e1d-49f9-b3da-cff2cf7dc7a1} + + {09875779-c73f-4138-9ed5-14af2b9d8337} - - {9d3b7c40-958a-4f6e-9ab6-72c3caea9591} + + {1dd9c7b9-1abc-412a-8e85-928c0d017beb} - - {6faf53e7-9be1-439f-92f9-16ab96c005b7} + + {525505fd-c8bb-453c-8cce-e11123499ae9} - - {e38cd6c5-b355-4bb5-bf65-bfd51fef296b} + + {10a04e38-fd13-476b-92ea-ebbe60803ef6} - - {30005d10-4213-441c-b18b-4dd47fcb8812} + + {7977cef5-28c7-40ca-be44-394d9f1b75de} - - {e901ef66-ddeb-4aff-9d7e-786d74a791e9} + + {3d8e58ec-0228-40ca-8ec8-66791a71bc08} - - {da82c6eb-abba-4b52-84bf-9f3758a169bf} + + {4759136c-9b08-4e32-91a9-a71c62f019c1} - - {6a6d7e33-2ce0-4b53-bcf6-41dc6dacdc5f} + + {c54c9382-0c06-4046-9e91-fb2a8019a22b} - - {e449786f-692c-4d6c-a292-b0720bf9e2a9} + + {0ac635a8-3c25-4b4b-b382-1bb8bc882c55} - - {46b49d69-ea42-467c-86d8-00611f2718ff} + + {db621634-f890-4852-b8dc-d540dffd433a} - - {2ec9179b-7320-47da-b170-f021d0279310} + + {2fb19b52-f018-4e95-8eca-ed230132ce0b} - - {83391c47-4a5b-4231-a9ef-29e119c5bb69} + + {d0a016cc-d3bc-449b-be29-29289dc550cb} - - {c27e3539-0070-40fa-b262-0ecd39f47919} + + {47bf6838-c9bf-4429-b55a-9a00339c630f} - - {afb847fc-853f-4e6f-bde6-91bf345369b4} + + {510de005-f0c8-48b9-b274-2424ccf8f7c7} - - {9f8f15a7-6e69-4a79-b644-a92ad815c600} + + {7cdf9989-f142-4f8a-a5ee-595fba1a354f} - - {25c5fe7f-23c0-4d44-b92d-ea4b127eec05} + + {ce08331e-176d-4071-8564-2cbda5a09f34} - - {0420f99f-468e-4ba5-abe3-960cc332b636} + + {a37df87a-dfda-47ab-a1be-1e49bdfecf44} - - {a40dcf17-855b-4965-b58d-6c7d24edc627} + + {a7469cda-54f2-4efc-8a58-6105c74845f9} - - {89bf5311-5686-4077-9b79-c6306dc1bd91} + + {f7c791f6-2cda-4702-88bd-458ea662e7ef} - - {3a4ca756-24d2-47e8-af7f-0e8a75933c4b} + + {956f49fb-0d6a-47d6-96a0-9d616e47e5f2} - - {58dbc5f6-cf3c-4d71-80c8-caefffb39beb} + + {698f4add-abac-46e5-a3f8-55d3a230b369} - - {0136e8e8-91ef-45c1-8661-476b5c20fc48} + + {942b8ea5-ce53-4e1e-ad7a-845991aaead6} - - {1979149f-6402-4985-b900-25a91f1168ac} + + {b0696621-8658-4918-b0f2-ba20acc26829} - - {10edfee7-8c10-41de-b8f3-424826d2614a} + + {cbe60970-f348-4a8b-8cee-d4cfebbe0d99} - - {02d83321-09fe-4a60-86d9-b1e8d5e165f4} + + {14a61eec-93ec-4e7c-b0bf-2ce23c3b782c} - - {a2663195-c4f2-4d5f-8d65-cfed54976e4c} + + {64bc6e43-c5e7-474f-be64-31df89e0034b} - - {10a22c13-763e-4054-bf6a-8f4b61697520} + + {d914384a-56bc-4829-977b-5900f01b5612} - - {04423080-121a-4982-8fd5-1d1d17157cbd} + + {da0b06b6-feab-44a9-bf9e-afb9e103eceb} + + + {7995c0ea-4972-4c52-854a-bd94cbdc6be9} + + + {336e3141-0276-4cd5-a836-585eef681b7b} - + + sdk\client + + + sdk\client + + + sdk\client + + + sdk\ebisusdk + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\engine + + + sdk\gameui + + + sdk\gameui + + + sdk\public + + + sdk\public + + + sdk\rtech + + + sdk\rtech + + + sdk\inputsystem + + + sdk\launcher + + + sdk\server + + + sdk\squirrel + + + sdk\squirrel + + + thirdparty\imgui + + + thirdparty\imgui + + + thirdparty\imgui + + + thirdparty\imgui + + + thirdparty\imgui + + + thirdparty\imgui + + + thirdparty\imgui + + + thirdparty\imgui + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\vgui + + + sdk\vgui + + + sdk\vguimatsurface + + + sdk\vpc + + + sdk\vpc + + + sdk\vpc + + + sdk\vphysics + + + windows + + + windows + + + windows + + core - + core - - shared\libraries\imgui - - - shared\libraries\imgui - - - shared\libraries\imgui - - - shared\libraries\imgui - - - shared\libraries\imgui - - - shared\libraries\imgui - - - shared\libraries\imgui - - - shared - - - hooks\src\chlclient - - - hooks\src\squirrel - - - hooks\src\cvengineserver - - - hooks\src\other - - - hooks\src\netchannel - - - hooks\src\iconvar - - - hooks\src\winapi - - - hooks\src - - - r5-sdk\src - - + core - - core + + thirdparty\imgui - - r5-sdk\src + + sdk\common - - shared\libraries\imgui + + sdk\mathlib - - gui\interface + + sdk\networksystem - - gui\interface + + sdk\squirrel - - gui + + sdk\server - - gui + + sdk\vpklib - - hooks\src\netchannel + + sdk\public - - hooks\src\other + + sdk\bsplib - - hooks\src\cserver + + sdk\mathlib - - r5-sdk\src - - - hooks\src\hoststate - - - hooks\src\cmatsystemsurface - - - hooks\src\cenginevgui - - - r5-sdk\src - - - hooks\src\originsdk + + sdk\mathlib - - shared\libraries\imgui\include + + sdk\client - - shared\libraries\imgui\include + + sdk\client - - shared\libraries\imgui\include + + sdk\client - - shared\libraries\imgui\include + + sdk\common - - shared\libraries\imgui\include + + sdk\ebisusdk - - shared\libraries\imgui\include + + sdk\engine - - shared\libraries\imgui\include + + sdk\engine - - shared\libraries\imgui\include + + sdk\engine - - shared\libraries\spdlog\include + + sdk\engine - - shared\libraries\spdlog\include + + sdk\engine - - shared\libraries\spdlog\include + + sdk\engine - - shared\libraries\spdlog\include + + sdk\engine - - shared\libraries\spdlog\include + + sdk\gameui - - shared\libraries\spdlog\include + + sdk\gameui - - shared\libraries\spdlog\include + + sdk\public\include - - shared\libraries\spdlog\include + + sdk\public\include - - shared\libraries\spdlog\include + + sdk\public\include - - shared\libraries\spdlog\include + + sdk\public\include - - shared\libraries\spdlog\include + + sdk\public\include - - shared\libraries\spdlog\include + + sdk\rtech - - shared\libraries\spdlog\include + + sdk\rtech - - shared\libraries\spdlog\include + + sdk\inputsystem - - shared\libraries\spdlog\include + + sdk\inputsystem - - shared\libraries\spdlog\include + + sdk\launcher - - shared\libraries\spdlog\include\cfg + + sdk\mathlib - - shared\libraries\spdlog\include\cfg + + sdk\networksystem - - shared\libraries\spdlog\include\cfg + + sdk\server - - shared\libraries\spdlog\include\cfg + + sdk\squirrel - - shared\libraries\spdlog\include\details + + sdk\squirrel - - shared\libraries\spdlog\include\details + + thirdparty\detours\include - - shared\libraries\spdlog\include\details + + thirdparty\detours\include - - shared\libraries\spdlog\include\details + + thirdparty\detours\include - - shared\libraries\spdlog\include\details + + thirdparty\detours\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\imgui\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\details + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\fmt + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\fmt + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\fmt + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\fmt + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\fmt + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\fmt\bundled + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\sinks - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt\bundled - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt\bundled - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt\bundled - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt\bundled - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt\bundled - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt\bundled - - shared\libraries\spdlog\include\sinks + + thirdparty\spdlog\include\fmt\bundled - - shared\include + + thirdparty\spdlog\include\fmt\bundled - - shared\include + + thirdparty\spdlog\include\fmt\bundled - - shared\include + + thirdparty\spdlog\include\fmt\bundled - - shared\include + + thirdparty\spdlog\include\fmt\bundled - - core\include + + thirdparty\spdlog\include\fmt\bundled - - shared\libraries\minhook\include + + thirdparty\spdlog\include\fmt\bundled - - hooks\include + + thirdparty\spdlog\include\details - - gui\include + + thirdparty\spdlog\include\details - - gui\include + + thirdparty\spdlog\include\details - - r5-sdk\include + + thirdparty\spdlog\include\details - - core\include + + thirdparty\spdlog\include\details - + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\details + + + thirdparty\spdlog\include\cfg + + + thirdparty\spdlog\include\cfg + + + thirdparty\spdlog\include\cfg + + + thirdparty\spdlog\include\cfg + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\tier0 + + + sdk\vgui + + + sdk\vgui + + + sdk\vgui + + + sdk\vguimatsurface + + + sdk\vpc + + + sdk\vpc + + + sdk\vpc + + + sdk\vpc + + + sdk\vphysics + + + windows + + + windows + + + windows + + core - - r5-sdk\include + + core - - r5-sdk\include + + core - - r5-sdk\include + + core - - r5-sdk\include + + sdk\common - - core\include + + sdk\mathlib - - shared\libraries\imgui\include + + sdk\networksystem - - gui\include + + sdk\networksystem - - gui\include + + sdk\mathlib - - - shared\include + + sdk\public\include - - r5-sdk\include + + sdk\squirrel - - r5-sdk\include + + core + + + sdk\server + + + sdk\tier0 + + + sdk\mathlib + + + sdk\vpklib + + + sdk\public\include + + + thirdparty\lzham\include + + + thirdparty\lzham\include + + + sdk\mathlib + + + sdk\bsplib + + + sdk\common - - core\resource - + + + + + sdk\resource\png + + + + + sdk\resource + \ No newline at end of file diff --git a/r5dev/resource/ico/sdklauncher_dbg.ico b/r5dev/resource/ico/sdklauncher_dbg.ico new file mode 100644 index 00000000..15e17194 Binary files /dev/null and b/r5dev/resource/ico/sdklauncher_dbg.ico differ diff --git a/r5dev/resource/ico/sdklauncher_rel.ico b/r5dev/resource/ico/sdklauncher_rel.ico new file mode 100644 index 00000000..94f2fae8 Binary files /dev/null and b/r5dev/resource/ico/sdklauncher_rel.ico differ diff --git a/r5dev/resource/png/lockedserver.png b/r5dev/resource/png/lockedserver.png new file mode 100644 index 00000000..2757113d Binary files /dev/null and b/r5dev/resource/png/lockedserver.png differ diff --git a/r5dev/BuildVersion.rc b/r5dev/resource/r5dev.rc similarity index 77% rename from r5dev/BuildVersion.rc rename to r5dev/resource/r5dev.rc index de49eb04..bd627999 100644 --- a/r5dev/BuildVersion.rc +++ b/r5dev/resource/r5dev.rc @@ -1,6 +1,6 @@ // Microsoft Visual C++ generated resource script. // -#include "resource.h" +#include "core/resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// @@ -16,7 +16,8 @@ // English (United States) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE 9, 1 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// @@ -24,18 +25,18 @@ LANGUAGE 9, 1 // TEXTINCLUDE // -1 TEXTINCLUDE +1 TEXTINCLUDE BEGIN - "resource.h\0" + "core/resource.h\0" END -2 TEXTINCLUDE +2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END -3 TEXTINCLUDE +3 TEXTINCLUDE BEGIN "\r\n" "\0" @@ -43,6 +44,14 @@ END #endif // APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// +// PNG +// + +IDB_PNG1 PNG "resource\\png\\lockedserver.png" + #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// @@ -57,3 +66,4 @@ END ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED + diff --git a/r5launcher/r5launcher.rc b/r5dev/resource/sdklauncher.rc similarity index 69% rename from r5launcher/r5launcher.rc rename to r5dev/resource/sdklauncher.rc index 170bbdd4..50453c05 100644 --- a/r5launcher/r5launcher.rc +++ b/r5dev/resource/sdklauncher.rc @@ -1,6 +1,9 @@ // Microsoft Visual C++ generated resource script. // -#include "resource.h" +#define USE_RES + +#ifdef USE_RES +#include "sdklauncher/sdklauncher_res.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// @@ -13,10 +16,10 @@ #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// -// German (Germany) resources +// English (United States) resources -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) -LANGUAGE LANG_GERMAN, SUBLANG_GERMAN +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #ifdef APSTUDIO_INVOKED @@ -27,7 +30,7 @@ LANGUAGE LANG_GERMAN, SUBLANG_GERMAN 1 TEXTINCLUDE BEGIN - "resource.h\0" + "sdklauncher/sdklauncher_res.h\0" END 2 TEXTINCLUDE @@ -52,9 +55,14 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDI_ICON1 ICON "r5reloaded.ico" -#endif // German (Germany) resources +#ifdef RELEASE +IDI_ICON1 ICON "resource\\ico\\sdklauncher_rel.ico" +#else +IDI_ICON1 ICON "resource\\ico\\sdklauncher_dbg.ico" +#endif + +#endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// @@ -64,8 +72,8 @@ IDI_ICON1 ICON "r5reloaded.ico" // // Generated from the TEXTINCLUDE 3 resource. // - +#endif // !APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED +#endif // !USE_RES diff --git a/r5dev/rtech/rtech.cpp b/r5dev/rtech/rtech.cpp new file mode 100644 index 00000000..a1d50172 --- /dev/null +++ b/r5dev/rtech/rtech.cpp @@ -0,0 +1,497 @@ +#include "core/stdafx.h" +#include "rtech/rtech.h" +#include "engine/sys_utils.h" + +/****************************************************************************** +------------------------------------------------------------------------------- +File : rtech.cpp +Date : 18:07:2021 +Author : Kawe Mazidjatari +Purpose: Implements the 'rtech_game' core utilities +------------------------------------------------------------------------------- +History: +- 18:07:2021 | 13:02 : Created by Kawe Mazidjatari +- 10:09:2021 | 18:22 : Implement 'StringToGuid' method +- 12:11:2021 | 14:41 : Add decompression method to ConCommand callback + +******************************************************************************/ + +//----------------------------------------------------------------------------- +// Purpose: calculate 'GUID' from input data +//----------------------------------------------------------------------------- +std::uint64_t __fastcall RTech::StringToGuid(const char* pData) +{ + std::uint32_t* v1; // r8 + std::uint64_t v2; // r10 + int v3; // er11 + std::uint32_t v4; // er9 + std::uint32_t i; // edx + std::uint64_t v6; // rcx + int v7; // er9 + int v8; // edx + int v9; // eax + std::uint32_t v10; // er8 + int v12; // ecx + std::uint32_t* a1 = (std::uint32_t*)pData; + + v1 = a1; + v2 = 0i64; + v3 = 0; + v4 = (*a1 - 45 * ((~(*a1 ^ 0x5C5C5C5Cu) >> 7) & (((*a1 ^ 0x5C5C5C5Cu) - 0x1010101) >> 7) & 0x1010101)) & 0xDFDFDFDF; + for (i = ~*a1 & (*a1 - 0x1010101) & 0x80808080; !i; i = v8 & 0x80808080) + { + v6 = v4; + v7 = v1[1]; + ++v1; + v3 += 4; + v2 = ((((std::uint64_t)(0xFB8C4D96501i64 * v6) >> 24) + 0x633D5F1 * v2) >> 61) ^ (((std::uint64_t)(0xFB8C4D96501i64 * v6) >> 24) + + 0x633D5F1 * v2); + v8 = ~v7 & (v7 - 0x1010101); + v4 = (v7 - 45 * ((~(v7 ^ 0x5C5C5C5Cu) >> 7) & (((v7 ^ 0x5C5C5C5Cu) - 0x1010101) >> 7) & 0x1010101)) & 0xDFDFDFDF; + } + v9 = -1; + v10 = (i & -(signed)i) - 1; + if (_BitScanReverse((unsigned long*)&v12, v10)) + { + v9 = v12; + } + return 0x633D5F1 * v2 + ((0xFB8C4D96501i64 * (std::uint64_t)(v4 & v10)) >> 24) - 0xAE502812AA7333i64 * (std::uint32_t)(v3 + v9 / 8); +} + +//----------------------------------------------------------------------------- +// Purpose: calculate 'decompressed' size and commit parameters +//----------------------------------------------------------------------------- +std::uint32_t __fastcall RTech::DecompressedSize(std::int64_t param_buf, std::uint8_t* file_buf, std::int64_t file_size, std::int64_t off_no_header, std::int64_t header_size) +{ + std::int64_t v8; // r9 + std::uint64_t v9; // r11 + char v10; // r8 + int v11; // er8 + std::int64_t v12; // rbx + unsigned int v13; // ebp + std::uint64_t v14; // rbx + std::int64_t v15; // rax + unsigned int v16; // er9 + std::uint64_t v17; // r12 + std::uint64_t v18; // r11 + std::uint64_t v19; // r10 + std::uint64_t v20; // rax + int v21; // ebp + std::uint64_t v22; // r10 + unsigned int v23; // er9 + std::int64_t v24; // rax + std::int64_t v25; // rsi + std::int64_t v26; // rdx + std::int64_t v28; // rdx + std::int64_t v29; // [rsp+48h] [rbp+18h] + std::int64_t result; // rax + + v29 = 0xFFFFFFi64; + *(std::uint64_t*)param_buf = (std::uint64_t)file_buf; + *(std::uint64_t*)(param_buf + 32) = off_no_header + file_size; + *(std::uint64_t*)(param_buf + 8) = 0i64; + *(std::uint64_t*)(param_buf + 24) = 0i64; + *(std::uint32_t*)(param_buf + 68) = 0; + *(std::uint64_t*)(param_buf + 16) = -1i64; + v8 = off_no_header + header_size + 8; + v9 = *(std::uint64_t*)((0xFFFFFFi64 & (off_no_header + header_size)) + file_buf); + *(std::uint64_t*)(param_buf + 80) = header_size; + *(std::uint64_t*)(param_buf + 72) = v8; + v10 = v9; + v9 >>= 6; + v11 = v10 & 0x3F; + *(std::uint64_t*)(param_buf + 40) = (1i64 << v11) | v9 & ((1i64 << v11) - 1); + v12 = *(std::uint64_t*)((0xFFFFFFi64 & v8) + file_buf) << (64 - ((std::uint8_t)v11 + 6)); + *(std::uint64_t*)(param_buf + 72) = v8 + ((std::uint8_t)(unsigned int)(v11 + 6) >> 3); + v13 = ((v11 + 6) & 7) + 13; + v14 = (0xFFFFFFFFFFFFFFFFui64 >> ((v11 + 6) & 7)) & ((v9 >> v11) | v12); + v15 = v29 & *(std::uint64_t*)(param_buf + 72); + v16 = (((std::uint8_t)v14 - 1) & 0x3F) + 1; + v17 = 0xFFFFFFFFFFFFFFFFui64 >> (64 - (std::uint8_t)v16); + *(uint64_t*)(param_buf + 48) = v17; + v18 = 0xFFFFFFFFFFFFFFFFui64 >> (64 - ((((v14 >> 6) - 1) & 0x3F) + 1)); + *(uint64_t*)(param_buf + 56) = v18; + v19 = (v14 >> 13) | (*(std::uint64_t*)(v15 + file_buf) << (64 - (std::uint8_t)v13)); + v20 = v13; + v21 = v13 & 7; + *(std::uint64_t*)(param_buf + 72) += v20 >> 3; + v22 = (0xFFFFFFFFFFFFFFFFui64 >> v21) & v19; + if (v17 == -1i64) + { + *(std::uint32_t*)(param_buf + 64) = 0; + *(std::uint64_t*)(param_buf + 88) = file_size; + } + else + { + v23 = v16 >> 3; + v24 = v29 & *(std::uint64_t*)(param_buf + 72); + *(std::uint32_t*)(param_buf + 64) = v23 + 1; + v25 = *(std::uint64_t*)(v24 + file_buf) & ((1i64 << (8 * ((std::uint8_t)v23 + 1))) - 1); + *(std::uint64_t*)(param_buf + 72) += v23 + 1; + *(std::uint64_t*)(param_buf + 88) = v25; + } + *(std::uint64_t*)(param_buf + 88) += off_no_header; + v26 = *(std::uint64_t*)(param_buf + 88); + *(std::uint64_t*)(param_buf + 96) = v22; + *(std::uint32_t*)(param_buf + 104) = v21; + *(std::uint64_t*)(param_buf + 112) = v17 + off_no_header - 6; + result = *(std::uint64_t*)(param_buf + 40); + *(std::uint32_t*)(param_buf + 108) = 0; + *(std::uint64_t*)(param_buf + 120) = v26; + *(std::uint64_t*)(param_buf + 128) = result; + if ((((std::uint8_t)(v14 >> 6) - 1) & 0x3F) != -1i64 && result - 1 > v18) + { + v28 = v26 - *(unsigned int*)(param_buf + 64); + *(std::uint64_t*)(param_buf + 128) = v18 + 1; + *(std::uint64_t*)(param_buf + 120) = v28; + } + return result; +} + +//----------------------------------------------------------------------------- +// Purpose: decompress input data +//----------------------------------------------------------------------------- +std::uint8_t __fastcall RTech::Decompress(std::int64_t* param_buffer, std::uint64_t file_size, std::uint64_t buf_size) +{ + char result; // al + std::int64_t v5; // r15 + std::int64_t v6; // r11 + std::uint32_t v7; // ebp + std::uint64_t v8; // rsi + std::uint64_t v9; // rdi + std::uint64_t v10; // r12 + std::int64_t v11; // r13 + std::uint32_t v12; // ecx + std::uint64_t v13; // rsi + std::uint64_t i; // rax + std::uint64_t v15; // r8 + std::int64_t v16; // r9 + int v17; // ecx + std::uint64_t v18; // rax + std::uint64_t v19; // rsi + std::int64_t v20; // r14 + int v21; // ecx + std::uint64_t v22; // r11 + int v23; // edx + std::int64_t v24; // rax + int v25; // er8 + std::uint32_t v26; // er13 + std::int64_t v27; // r10 + std::int64_t v28; // rax + std::uint64_t* v29; // r10 + std::uint64_t v30; // r9 + std::uint64_t v31; // r10 + std::uint64_t v32; // r8 + std::uint64_t v33; // rax + std::uint64_t v34; // rax + std::uint64_t v35; // rax + std::uint64_t v36; // rcx + std::uint64_t v37; // rdx + std::uint64_t v38; // r14 + std::uint64_t v39; // r11 + char v40; // cl + std::uint64_t v41; // rsi + std::uint64_t v42; // rcx + std::uint64_t v43; // r8 + int v44; // er11 + std::uint8_t v45; // r9 + std::uint64_t v46; // rcx + std::uint64_t v47; // rcx + std::uint64_t v48; // r9 + std::uint64_t l; // r8 + std::uint32_t v50; // er9 + std::uint64_t v51; // r8 + std::uint64_t v52; // rdx + std::uint64_t k; // r8 + char* v54; // r10 + std::uint64_t v55; // rdx + std::uint32_t v56; // er14 + std::uint64_t* v57; // rdx + std::uint64_t* v58; // r8 + char v59; // al + std::uint64_t v60; // rsi + std::uint64_t v61; // rax + std::uint64_t v62; // r9 + int v63; // er10 + std::uint8_t v64; // cl + std::uint64_t v65; // rax + std::uint32_t v66; // er14 + std::uint32_t j; // ecx + std::uint64_t v68; // rax + std::uint64_t v69; // rcx + std::uint64_t v70; // [rsp+0h] [rbp-58h] + int v71; // [rsp+60h] [rbp+8h] + std::uint64_t v74; // [rsp+78h] [rbp+20h] + + if (file_size < param_buffer[11]) + { + return 0; + } + v5 = param_buffer[10]; + if (buf_size < param_buffer[7] + (v5 & (std::uint64_t)~param_buffer[7]) + 1 && buf_size < param_buffer[5]) + { + return 0; + } + v6 = param_buffer[1]; + v7 = *((std::uint32_t*)param_buffer + 26); + v8 = param_buffer[12]; + v9 = param_buffer[9]; + v10 = param_buffer[14]; + v11 = *param_buffer; + if (param_buffer[15] < v10) + { + v10 = param_buffer[15]; + } + v12 = *((std::uint32_t*)param_buffer + 27); + v74 = v11; + v70 = v6; + v71 = v12; + if (!v7) + { + goto LABEL_11; + } + v13 = (*(std::uint64_t*)((v9 & param_buffer[2]) + v11) << (64 - (std::uint8_t)v7)) | v8; + for (i = v7; ; i = v7) + { + v7 &= 7u; + v9 += i >> 3; + v12 = v71; + v8 = (0xFFFFFFFFFFFFFFFFui64 >> v7) & v13; + LABEL_11: + v15 = (std::uint64_t)v12 << 8; + v16 = v12; + v17 = *((std::uint8_t*)&LUT_0 + (std::uint8_t)v8 + v15 + 512); + v18 = (std::uint8_t)v8 + v15; + v7 += v17; + v19 = v8 >> v17; + v20 = (std::uint32_t)*((char*)&LUT_0 + v18); + if (*((char*)&LUT_0 + v18) < 0) + { + v56 = -(int)v20; + v57 = (std::uint64_t*)(v11 + (v9 & param_buffer[2])); + v71 = 1; + v58 = (std::uint64_t*)(v6 + (v5 & param_buffer[3])); + if (v56 == *((std::uint8_t*)&LUT_0 + v16 + 1248)) + { + if ((~v9 & param_buffer[6]) < 0xF || (param_buffer[7] & (std::uint64_t)~v5) < 0xF || (std::uint64_t)(param_buffer[5] - v5) < 0x10) + { + v56 = 1; + } + v59 = v19; + v60 = v19 >> 3; + v61 = v59 & 7; + v62 = v60; + if (v61) + { + v63 = *((std::uint8_t*)&LUT_0 + v61 + 1232); + v64 = *((std::uint8_t*)&LUT_0 + v61 + 1240); + } + else + { + v62 = v60 >> 4; + v65 = v60 & 0xF; + v7 += 4; + v63 = *((std::uint32_t*)&LUT_0 + v65 + 288); + v64 = *((std::uint8_t*)&LUT_0 + v65 + 1216); + } + v7 += v64 + 3; + v19 = v62 >> v64; + v66 = v63 + (v62 & ((1 << v64) - 1)) + v56; + for (j = v66 >> 3; j; --j) + { + v68 = *v57++; + *v58++ = v68; + } + if ((v66 & 4) != 0) + { + *(std::uint32_t*)v58 = *(std::uint32_t*)v57; + v58 = (std::uint64_t*)((char*)v58 + 4); + v57 = (std::uint64_t*)((char*)v57 + 4); + } + if ((v66 & 2) != 0) + { + *(std::uint16_t*)v58 = *(std::uint16_t*)v57; + v58 = (std::uint64_t*)((char*)v58 + 2); + v57 = (std::uint64_t*)((char*)v57 + 2); + } + if ((v66 & 1) != 0) + { + *(std::uint8_t*)v58 = *(std::uint8_t*)v57; + } + v9 += v66; + v5 += v66; + } + else + { + *v58 = *v57; + v58[1] = v57[1]; + v9 += v56; + v5 += v56; + } + } + else + { + v21 = v19 & 0xF; + v71 = 0; + v22 = ((std::uint64_t)(std::uint32_t)v19 >> (((std::uint32_t)(v21 - 31) >> 3) & 6)) & 0x3F; + v23 = 1 << (v21 + ((v19 >> 4) & ((24 * (((std::uint32_t)(v21 - 31) >> 3) & 2)) >> 4))); + v7 += (((std::uint32_t)(v21 - 31) >> 3) & 6) + *((std::uint8_t*)&LUT_0 + v22 + 1088) + v21 + ((v19 >> 4) & ((24 * (((std::uint32_t)(v21 - 31) >> 3) & 2)) >> 4)); + v24 = param_buffer[3]; + v25 = 16 * (v23 + ((v23 - 1) & (v19 >> ((((std::uint32_t)(v21 - 31) >> 3) & 6) + *((std::uint8_t*)&LUT_0 + v22 + 1088))))); + v19 >>= (((std::uint32_t)(v21 - 31) >> 3) & 6) + *((std::uint8_t*)&LUT_0 + v22 + 1088) + v21 + ((v19 >> 4) & ((24 * (((std::uint32_t)(v21 - 31) >> 3) & 2)) >> 4)); + v26 = v25 + *((std::uint8_t*)&LUT_0 + v22 + 1024) - 16; + v27 = v24 & (v5 - v26); + v28 = v70 + (v5 & v24); + v29 = (std::uint64_t*)(v70 + v27); + if ((std::uint32_t)v20 == 17) + { + v40 = v19; + v41 = v19 >> 3; + v42 = v40 & 7; + v43 = v41; + if (v42) + { + v44 = *((std::uint8_t*)&LUT_0 + v42 + 1232); + v45 = *((std::uint8_t*)&LUT_0 + v42 + 1240); + } + else + { + v7 += 4; + v46 = v41 & 0xF; + v43 = v41 >> 4; + v44 = *((std::uint32_t*)&LUT_0 + v46 + 288); + v45 = *((std::uint8_t*)&LUT_0 + v46 + 1216); + if (v74 && v7 + v45 >= 0x3D) + { + v47 = v9++ & param_buffer[2]; + v43 |= (std::uint64_t)*(std::uint8_t*)(v47 + v74) << (61 - (std::uint8_t)v7); + v7 -= 8; + } + } + v7 += v45 + 3; + v19 = v43 >> v45; + v48 = ((std::uint32_t)v43 & ((1 << v45) - 1)) + v44 + 17; + v5 += v48; + if (v26 < 8) + { + v50 = v48 - 13; + v5 -= 13i64; + if (v26 == 1) + { + v51 = *(std::uint8_t*)v29; + v52 = 0i64; + for (k = 0x101010101010101i64 * v51; (std::uint32_t)v52 < v50; v52 = (std::uint32_t)(v52 + 8)) + { + *(std::uint64_t*)(v52 + v28) = k; + } + } + else + { + if (v50) + { + v54 = (char*)v29 - v28; + v55 = v50; + do + { + *(std::uint8_t*)v28 = v54[v28]; + ++v28; + --v55; + } while (v55); + } + } + } + else + { + for (l = 0i64; (std::uint32_t)l < (std::uint32_t)v48; l = (std::uint32_t)(l + 8)) + { + *(std::uint64_t*)(l + v28) = *(std::uint64_t*)((char*)v29 + l); + } + } + } + else + { + v5 += v20; + *(std::uint64_t*)v28 = *v29; + *(std::uint64_t*)(v28 + 8) = v29[1]; + } + v11 = v74; + } + if (v9 >= v10) + { + break; + } + LABEL_29: + v6 = v70; + v13 = (*(std::uint64_t*)((v9 & param_buffer[2]) + v11) << (64 - (std::uint8_t)v7)) | v19; + } + if (v5 != param_buffer[16]) + { + goto LABEL_25; + } + v30 = param_buffer[5]; + if (v5 == v30) + { + result = 1; + goto LABEL_69; + } + v31 = param_buffer[6]; + v32 = *((std::uint32_t*)param_buffer + 16); + v33 = v31 & -(std::int64_t)v9; + v19 >>= 1; + ++v7; + if (v32 > v33) + { + v9 += v33; + v34 = param_buffer[14]; + if (v9 > v34) + { + param_buffer[14] = v31 + v34 + 1; + } + } + v35 = v9 & param_buffer[2]; + v9 += v32; + v36 = v5 + param_buffer[7] + 1; + v37 = *(std::uint32_t*)(v35 + v11) & ((1i64 << (8 * (std::uint8_t)v32)) - 1); + v38 = v37 + param_buffer[11]; + v39 = v37 + param_buffer[15]; + param_buffer[11] = v38; + param_buffer[15] = v39; + if (v36 >= v30) + { + v36 = v30; + param_buffer[15] = v32 + v39; + } + param_buffer[16] = v36; + if (file_size >= v38 && buf_size >= v36) + { + LABEL_25: + v10 = param_buffer[14]; + if (v9 >= v10) + { + v9 = ~param_buffer[6] & (v9 + 7); + v10 += param_buffer[6] + 1; + param_buffer[14] = v10; + } + if (param_buffer[15] < v10) + { + v10 = param_buffer[15]; + } + goto LABEL_29; + } + v69 = param_buffer[14]; + if (v9 >= v69) + { + v9 = ~v31 & (v9 + 7); + param_buffer[14] = v69 + v31 + 1; + } + *((std::uint32_t*)param_buffer + 27) = v71; + result = 0; + param_buffer[12] = v19; + *((std::uint32_t*)param_buffer + 26) = v7; +LABEL_69: + param_buffer[10] = v5; + param_buffer[9] = v9; + return result; +} +/////////////////////////////////////////////////////////////////////////////// +RTech* g_pRtech = new RTech(); diff --git a/r5dev/rtech/rtech.h b/r5dev/rtech/rtech.h new file mode 100644 index 00000000..534223a8 --- /dev/null +++ b/r5dev/rtech/rtech.h @@ -0,0 +1,176 @@ +#pragma once + +#define PAK_HEADER_SIZE 0x80 +#define PAK_PARAM_SIZE 0xB0 +#define DCMP_BUF_SIZE 0x400000 + +namespace +{ + /*unk_141313180*/ + const unsigned char LUT_0[0x720] = + { + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0B, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0xF7, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF3, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0E, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0x09, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF1, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0D, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0xF7, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF2, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0F, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0x0A, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF0, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0C, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x09, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0E, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0B, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0A, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x10, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0C, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x09, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0F, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0D, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0A, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0xFF, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x07, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x07, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x06, 0x00, 0x08, 0x00, 0x01, 0x00, 0x08, 0x00, 0x0B, + 0x00, 0x08, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x08, 0x00, 0x03, 0x00, 0x08, 0x00, 0x0E, + 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x02, 0x00, 0x08, 0x00, 0x0D, + 0x00, 0x08, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0F, + 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, + 0x4A, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, + 0xCA, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, + 0x4A, 0x01, 0x00, 0x00, 0x6A, 0x01, 0x00, 0x00, 0x8A, 0x01, 0x00, 0x00, 0xAA, 0x01, 0x00, 0x00, + 0xAA, 0x03, 0x00, 0x00, 0xAA, 0x05, 0x00, 0x00, 0xAA, 0x25, 0x00, 0x00, 0xAA, 0x25, 0x02, 0x00, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x09, 0x09, 0x0D, 0x11, 0x15, + 0x00, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x2A, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x05, 0x05, + 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, + 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, + 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, + 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, + 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, + 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, + 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xF1, 0x1D, 0xC1, 0xF6, 0x7F, 0x00, 0x00, + 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, + 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, + 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, + 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, + 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, + 0x00, 0x70, 0x95, 0xB6, 0x00, 0x70, 0x95, 0xB6, 0x00, 0x70, 0x95, 0xB6, 0x00, 0x70, 0x95, 0xB6, + 0xA9, 0xAA, 0x2A, 0x3D, 0xA9, 0xAA, 0x2A, 0x3D, 0xA9, 0xAA, 0x2A, 0x3D, 0xA9, 0xAA, 0x2A, 0x3D, + 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, + 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, + 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, + 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, + 0x4C, 0x39, 0x56, 0x75, 0x42, 0x52, 0x65, 0x75, 0x70, 0x35, 0x31, 0x77, 0x4C, 0x51, 0x64, 0x61, + }; +} + +struct rpak_h +{ + std::uint32_t m_nMagic; // 'RPak' + std::uint16_t m_nVersion; // R2 = '7' R5 = '8' + std::uint8_t m_nFlags[0x2]; // + std::uint8_t m_nHash[0x10]; // + std::uint64_t m_nSizeDisk; // Compressed size + std::uint64_t m_nEmbeddedStarpakOffset; // + std::uint8_t unk0[0x8]; // + std::uint64_t m_nSizeMemory; // Decompressed size + std::uint64_t m_nEmbeddedStarpakSize; // + std::uint8_t unk1[0x8]; // + + std::uint16_t m_nStarpakReferenceSize; // + std::uint16_t m_nStarpakOptReferenceSize; // + std::uint16_t m_nVirtualSegmentCount; // * 0x10 + std::uint16_t m_nVirtualSegmentBlockCount; // * 0xC + + std::uint32_t m_nPatchIndex; // + + std::uint32_t m_nUnknownThirdBlockCount; // + std::uint32_t m_nAssetEntryCount; // File entry count + std::uint32_t m_nUnknownFifthBlockCount; // + std::uint32_t m_nUnknownSixedBlockCount; // + + std::uint8_t unk2[0x1C]; // +}; + +namespace +{ + /* ==== RTECH =========================================================================================================================================================== */ + //DWORD64 p_RTech_Decompress = FindPatternV2("r5apex.exe", (const unsigned char*)"\x4C\x89\x44\x24\x18\x48\x89\x54\x24\x10\x53\x48\x83\xEC\x50\x48", "xxxxxxxxxxxxxxxx"); + //char (*RTech_Decompress)(int64_t* parameter, std::uint64_t input, std::uint64_t output) = (char (*)(std::int64_t*, std::uint64_t, std::uint64_t))p_RTech_Decompress; /*4C 89 44 24 18 48 89 54 24 10 53 48 83 EC 50 48*/ + + //DWORD64 p_RTech_DecompressedSize = FindPatternV2("r5apex.exe", (const unsigned char*)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x18\x48\x89\x74\x24\x20\x48\x89\x54\x24\x10\x57\x41\x54\x41\x55\x41\x56\x41\x57\x4C\x8B\x74", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + //std::int64_t (*RTech_DecompressedSize)(std::int64_t parameter, std::uint8_t* input, std::int64_t magic, std::int64_t a4, std::int64_t a5) = (std::int64_t (*)(std::int64_t, std::uint8_t*, std::int64_t, std::int64_t, std::int64_t))p_RTech_DecompressedSize; /*48 89 5C 24 08 48 89 6C 24 18 48 89 74 24 20 48 89 54 24 10 57 41 54 41 55 41 56 41 57 4C 8B 74*/ +} + +class RTech +{ +public: + std::uint64_t __fastcall StringToGuid(const char* pData); + std::uint8_t __fastcall Decompress(std::int64_t* params, std::uint64_t file_size, std::uint64_t buffer_size); + std::uint32_t __fastcall DecompressedSize(std::int64_t param_buf, std::uint8_t* file_buf, std::int64_t file_size, std::int64_t off_no_header, std::int64_t header_size); +}; + +/////////////////////////////////////////////////////////////////////////////// +extern RTech* g_pRtech; diff --git a/r5dev/rtech/stryder.cpp b/r5dev/rtech/stryder.cpp new file mode 100644 index 00000000..d8101c70 --- /dev/null +++ b/r5dev/rtech/stryder.cpp @@ -0,0 +1,3 @@ +#include "core/stdafx.h" +#include "rtech/stryder.h" +//TODO diff --git a/r5dev/rtech/stryder.h b/r5dev/rtech/stryder.h new file mode 100644 index 00000000..ad1e7400 --- /dev/null +++ b/r5dev/rtech/stryder.h @@ -0,0 +1,27 @@ +#pragma once +#include "tier0/basetypes.h" + +namespace +{ + /* ==== STRYDER ================================================================================================================================================ */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_Stryder_StitchRequest = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\xC4\x53\x57\x41\x56\x48\x81\xEC\x20", "xxxxxxxxxxx"); + void* (*Stryder_StitchRequest)(void* a1) = (void* (*)(void*))p_Stryder_StitchRequest.GetPtr(); /*48 8B C4 53 57 41 56 48 81 EC 20*/ +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_Stryder_StitchRequest = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x10\x48\x89\x74\x24\x18\x57\x48\x83\xEC\x20\x48\x8B\xF9\xE8\xB4", "xxxxxxxxxxxxxxxxxxxxxxxxx"); + void* (*Stryder_StitchRequest)(void* a1) = (void* (*)(void*))p_Stryder_StitchRequest.GetPtr(); /*48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 48 8B F9 E8 B4*/ +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +class HStryder : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: Stryder_StitchRequest : 0x" << std::hex << std::uppercase << p_Stryder_StitchRequest.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HStryder); \ No newline at end of file diff --git a/r5launcher/r5launcher.vcxproj b/r5dev/sdklauncher.vcxproj similarity index 79% rename from r5launcher/r5launcher.vcxproj rename to r5dev/sdklauncher.vcxproj index 197a31a7..bbd178d4 100644 --- a/r5launcher/r5launcher.vcxproj +++ b/r5dev/sdklauncher.vcxproj @@ -22,36 +22,38 @@ 16.0 Win32Proj {18f8c75e-3844-4aa6-ab93-980a08253519} - r5launcher + sdklauncher 10.0 + sdklauncher Application true - v142 + v143 Unicode Application false - v142 + v143 true Unicode Application true - v142 + v143 MultiByte + Static Application false - v142 + v143 true MultiByte - false + Static @@ -66,11 +68,10 @@ - + - @@ -81,19 +82,19 @@ true - $(SolutionDir)external\detours\include;$(SolutionDir)external\spdlog\include;$(IncludePath) - $(SolutionDir)external\detours\libs;$(LibraryPath) - $(SolutionDir)build\$(ProjectName)\$(Configuration)\ + $(SolutionDir)r5dev\;$(IncludePath); + $(SolutionDir)r5dev\thirdparty\detours\libs;$(LibraryPath); + r5reloaded $(SolutionDir)bin\$(Configuration)\ - launcher + $(SolutionDir)build\$(ProjectName)\$(Configuration)\ false - $(SolutionDir)external\detours\include;$(SolutionDir)external\spdlog\include;$(IncludePath) - $(SolutionDir)external\detours\libs;$(LibraryPath) + $(SolutionDir)r5dev\;$(IncludePath); + $(SolutionDir)r5dev\thirdparty\detours\libs;$(LibraryPath); + r5reloaded $(SolutionDir)bin\$(Configuration)\ $(SolutionDir)build\$(ProjectName)\$(Configuration)\ - launcher @@ -129,11 +130,12 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - stdcpp17 - stdc17 + + Use - pch.h - MultiThreadedDebug + stdcpp17 + core\stdafx.h + /D SDKLAUNCHER %(AdditionalOptions) Console @@ -141,8 +143,7 @@ detours.lib;%(AdditionalDependencies) - - + del "..\..\..\bin\r5reloaded.exe" && copy /Y "$(TargetPath)" "..\..\..\bin\$(TargetFileName)" @@ -153,14 +154,20 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - stdcpp17 - stdc17 - AnySuitable + + Speed + true true true + true + + + AnySuitable Use - pch.h + stdcpp17 + core\stdafx.h + /D SDKLAUNCHER %(AdditionalOptions) Console @@ -170,27 +177,27 @@ detours.lib;%(AdditionalDependencies) - del "$(SolutionDir)bin\$(Configuration)\r5reloaded.exe" -rename "$(TargetPath)" "r5reloaded.exe" + del "..\..\..\r5reloaded.exe" && copy /Y "$(TargetPath)" "..\..\..\$(TargetFileName)" - - + Create Create + - - - + - + + - + + + diff --git a/r5launcher/r5launcher.vcxproj.filters b/r5dev/sdklauncher.vcxproj.filters similarity index 74% rename from r5launcher/r5launcher.vcxproj.filters rename to r5dev/sdklauncher.vcxproj.filters index 38adabbd..5a7f2de0 100644 --- a/r5launcher/r5launcher.vcxproj.filters +++ b/r5dev/sdklauncher.vcxproj.filters @@ -15,31 +15,34 @@ - + Source Files - + Source Files - - Header Files - - - Header Files - - - Header Files - - - - + Resource Files - + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + Resource Files diff --git a/r5launcher/main.cpp b/r5dev/sdklauncher/sdklauncher.cpp similarity index 93% rename from r5launcher/main.cpp rename to r5dev/sdklauncher/sdklauncher.cpp index 0677f381..7e36fb69 100644 --- a/r5launcher/main.cpp +++ b/r5dev/sdklauncher/sdklauncher.cpp @@ -1,5 +1,5 @@ -#include "pch.h" -#include "main.h" +#include "core/stdafx.h" +#include "sdklauncher.h" //----------------------------------------------------------------------------- // Print the error message to the console if any. @@ -13,7 +13,7 @@ void PrintLastError() 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); - spdlog::error("{}", messageBuffer); + spdlog::error("{}\n", messageBuffer); LocalFree(messageBuffer); } } @@ -56,7 +56,7 @@ bool LaunchR5Apex(LAUNCHMODE lMode, LAUNCHSTATE lState) } cfgFile.close(); // Close cfg file. - WorkerDll = currentDirectory + "\\r5dev.dll"; // Get path to worker dll. + WorkerDll = currentDirectory + "\\bin\\r5apexvtxd.dll"; // Get path to worker dll. GameDirectory = currentDirectory + "\\r5apex.exe"; // Get path to game executeable. StartupCommandLine = currentDirectory + "\\r5apex.exe " + CommandLineArguments; // Setup startup command line string. @@ -81,7 +81,7 @@ bool LaunchR5Apex(LAUNCHMODE lMode, LAUNCHSTATE lState) } cfgFile.close(); // Close cfg file. - WorkerDll = currentDirectory + "\\r5detours.dll"; // Get path to worker dll. + WorkerDll = currentDirectory + "\\r5apexsdkd64.dll"; // Get path to worker dll. GameDirectory = currentDirectory + "\\r5apex.exe"; // Get path to game executeable. StartupCommandLine = currentDirectory + "\\r5apex.exe " + CommandLineArguments; // Setup startup command line string. @@ -95,8 +95,8 @@ bool LaunchR5Apex(LAUNCHMODE lMode, LAUNCHSTATE lState) if (cfgFile.good() && cfgFile) // Does the cfg file exist? { std::stringstream ss; - ss << cfgFile.rdbuf(); // Read ifstream buffer into stringstream. - CommandLineArguments = ss.str(); // Get all the contents of the cfg file. + ss << cfgFile.rdbuf(); // Read ifstream buffer into stringstream. + CommandLineArguments = ss.str(); // Get all the contents of the cfg file. } else { @@ -219,9 +219,9 @@ int main(int argc, char* argv[], char* envp[]) spdlog::warn("All FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY ConVar's/ConCommand's will be enabled.\n"); spdlog::warn("Connected clients will be able to set and execute anything flagged FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY.\n"); std::cout << "--------------------------------------------------------------------------------------------------------" << std::endl; - spdlog::warn("Use DEBUG [1] for development and debugging purposes.\n"); - spdlog::warn("Use RELEASE [2] for normal playing and server hosting.\n"); - spdlog::warn("Use DEDICATED [3] for running a dedicated server.\n"); + spdlog::warn("Use DEBUG [1] for research and development purposes.\n"); + spdlog::warn("Use RELEASE [2] for playing and server hosting purposes.\n"); + spdlog::warn("Use DEDICATED [3] for running and hosting a dedicated server.\n"); std::cout << "--------------------------------------------------------------------------------------------------------" << std::endl; spdlog::info("Enter 1 for DEBUG. Enter 2 for RELEASE. Enter 3 for DEDICATED: "); @@ -266,9 +266,8 @@ int main(int argc, char* argv[], char* envp[]) return EXIT_FAILURE; } } - spdlog::error("R5Reloaded requires numerical input to launch.\n"); Sleep(5000); return EXIT_FAILURE; -} \ No newline at end of file +} diff --git a/r5launcher/main.h b/r5dev/sdklauncher/sdklauncher.h similarity index 100% rename from r5launcher/main.h rename to r5dev/sdklauncher/sdklauncher.h diff --git a/r5launcher/resource.h b/r5dev/sdklauncher/sdklauncher_res.h similarity index 94% rename from r5launcher/resource.h rename to r5dev/sdklauncher/sdklauncher_res.h index 13375343..cd449a78 100644 --- a/r5launcher/resource.h +++ b/r5dev/sdklauncher/sdklauncher_res.h @@ -1,6 +1,6 @@ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. -// Used by r5launcher.rc +// Used by r5launch.rc // #define IDI_ICON1 101 diff --git a/r5dev/server/IVEngineServer.cpp b/r5dev/server/IVEngineServer.cpp new file mode 100644 index 00000000..a46279bb --- /dev/null +++ b/r5dev/server/IVEngineServer.cpp @@ -0,0 +1,70 @@ +//=============================================================================// +// +// Purpose: Interface the engine exposes to the game DLL +// +//=============================================================================// + +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "engine/sys_utils.h" +#include "server/IVEngineServer.h" +#include "client/client.h" + +//----------------------------------------------------------------------------- +// Purpose: sets the persistence var in the CClient instance to 'ready' +//----------------------------------------------------------------------------- +bool HIVEngineServer_PersistenceAvailable(void* entidx, int clientidx) +{ + CClient* pClient = g_pClient->GetClientInstance(clientidx); // Get client instance. + std::uintptr_t targetInstance = (std::uintptr_t)pClient; + *(char*)(targetInstance + g_dwPersistenceVar) = (char)0x5; // Set the client instance to 'ready'. + + if (!g_bIsPersistenceVarSet[clientidx] && sv_showconnecting->m_pParent->m_iValue > 0) + { + void* clientNamePtr = (void**)(((std::uintptr_t)pClient->GetNetChan()) + 0x1A8D); // Get client name from netchan. + std::string clientName((char*)clientNamePtr, 32); // Get full name. + std::int64_t originID = pClient->m_iOriginID; + std::int64_t clientID = static_cast(pClient->m_iUserID + 1); + + std::string ipAddress = "null"; // If this stays null they modified the packet somehow. + ADDRESS ipAddressField = ADDRESS(((std::uintptr_t)pClient->GetNetChan()) + 0x1AC0); // Get client ip from netchan. + if (ipAddressField) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue()); + + ipAddress = ss.str(); + } + + DevMsg(eDLL_T::SERVER, "\n"); + DevMsg(eDLL_T::SERVER, "______________________________________________________________\n"); + DevMsg(eDLL_T::SERVER, "] CLIENT_INSTANCE_DETAILS ------------------------------------\n"); + DevMsg(eDLL_T::SERVER, "] INDEX: | '#%d'\n", clientidx); + DevMsg(eDLL_T::SERVER, "] NAME : | '%s'\n", clientName.c_str()); + DevMsg(eDLL_T::SERVER, "] OID : | '%lld'\n", originID); + DevMsg(eDLL_T::SERVER, "] UID : | '%lld'\n", clientID); + DevMsg(eDLL_T::SERVER, "] IPADR: | '%s'\n", ipAddress.c_str()); + DevMsg(eDLL_T::SERVER, "--------------------------------------------------------------\n"); + DevMsg(eDLL_T::SERVER, "\n"); + + g_bIsPersistenceVarSet[clientidx] = true; + } + /////////////////////////////////////////////////////////////////////////// + return IVEngineServer_PersistenceAvailable(entidx, clientidx); +} + +void IVEngineServer_Attach() +{ + DetourAttach((LPVOID*)&IVEngineServer_PersistenceAvailable, &HIVEngineServer_PersistenceAvailable); +} + +void IVEngineServer_Detach() +{ + DetourDetach((LPVOID*)&IVEngineServer_PersistenceAvailable, &HIVEngineServer_PersistenceAvailable); +} + +/////////////////////////////////////////////////////////////////////////////// +bool g_bIsPersistenceVarSet[128]; diff --git a/r5dev/server/IVEngineServer.h b/r5dev/server/IVEngineServer.h new file mode 100644 index 00000000..c7d7ed26 --- /dev/null +++ b/r5dev/server/IVEngineServer.h @@ -0,0 +1,30 @@ +#pragma once + +namespace +{ + /* ==== CVENGINESERVER ================================================================================================================================================== */ + ADDRESS p_IVEngineServer_PersistenceAvailable = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x3B\x15\x00\x00\x00\x00\x7D\x33", "xx????xx"); + bool (*IVEngineServer_PersistenceAvailable)(void* entidx, int clientidx) = (bool (*)(void*, int))p_IVEngineServer_PersistenceAvailable.GetPtr(); /*3B 15 ?? ?? ?? ?? 7D 33*/ +} + +/////////////////////////////////////////////////////////////////////////////// +bool HIVEngineServer_PersistenceAvailable(void* entidx, int clientidx); + +void IVEngineServer_Attach(); +void IVEngineServer_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +extern bool g_bIsPersistenceVarSet[128]; + +/////////////////////////////////////////////////////////////////////////////// +class HVEngineServer : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: IVEngineServer::PersistenceAvailable : 0x" << std::hex << std::uppercase << p_IVEngineServer_PersistenceAvailable.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HVEngineServer); diff --git a/r5dev/server/server.cpp b/r5dev/server/server.cpp new file mode 100644 index 00000000..682db81c --- /dev/null +++ b/r5dev/server/server.cpp @@ -0,0 +1,121 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "engine/sys_utils.h" +#include "server/server.h" +#include "client/client.h" +#include "networksystem/r5net.h" +#include "public/include/bansystem.h" + +//----------------------------------------------------------------------------- +// Purpose: checks is particular client is banned on the comp server +//----------------------------------------------------------------------------- +void IsClientBanned(R5Net::Client* pR5net, const std::string svIPAddr, std::int64_t nNucleusID) +{ + std::string svError = std::string(); + bool bCompBanned = pR5net && pR5net->GetClientIsBanned(svIPAddr, nNucleusID, svError); + if (bCompBanned) + { + while (bCompBanned) + { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. + { + CClient* pClient = g_pClient->GetClientInstance(i); // Get client instance. + if (!pClient) // Client instance valid? + { + continue; + } + + if (!pClient->GetNetChan()) // Netchan valid? + { + continue; + } + + std::int64_t nOriginID = pClient->m_iOriginID; // Get originID. + if (nOriginID != nNucleusID) // See if they match. + { + continue; + } + + g_pBanSystem->AddConnectionRefuse(svError, pClient->m_iUserID + 1); // Add to the vector. + bCompBanned = false; + break; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: client to server authentication +//----------------------------------------------------------------------------- +void* HCServer_Authenticate(void* pServer, user_creds* pInpacket) +{ + std::string svIpAddress = "null"; + ADDRESS ipAddressField = ADDRESS(&pInpacket->m_nIpAddr); + + if (ipAddressField && ipAddressField.GetValue() != 0x0) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue()); + + svIpAddress = ss.str(); + } + + if (sv_showconnecting->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::SERVER, "\n"); + DevMsg(eDLL_T::SERVER, "______________________________________________________________\n"); + DevMsg(eDLL_T::SERVER, "] AUTHENTICATION_DETAILS -------------------------------------\n"); + DevMsg(eDLL_T::SERVER, "] UserID : | '%s'\n", pInpacket->m_nUserID); + DevMsg(eDLL_T::SERVER, "] OriginID : | '%lld'\n", pInpacket->m_nNucleusID); + DevMsg(eDLL_T::SERVER, "] IP-ADDR : | '%s'\n", svIpAddress.c_str()); + DevMsg(eDLL_T::SERVER, "--------------------------------------------------------------\n"); + } + + if (g_pBanSystem->IsBanListValid()) // Is the banlist vector valid? + { + if (g_pBanSystem->IsBanned(svIpAddress, pInpacket->m_nNucleusID)) // Is the client trying to connect banned? + { + CServer_RejectConnection(pServer, *(unsigned int*)((std::uintptr_t)pServer + 0xC), pInpacket, "You have been banned from this Server."); // RejectConnection for the client. + + if (sv_showconnecting->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::SERVER, "] NOTICE : | THIS CLIENT IS BANNED!\n"); + DevMsg(eDLL_T::SERVER, "--------------------------------------------------------------\n\n"); + } + return nullptr; + } + } + if (sv_showconnecting->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::SERVER, "\n"); + } + + if (g_bCheckCompBanDB) + { + if (g_pR5net) + { + std::thread th(IsClientBanned, g_pR5net, svIpAddress, pInpacket->m_nNucleusID); + th.detach(); + } + } + + return CServer_Authenticate(pServer, pInpacket); +} + +/////////////////////////////////////////////////////////////////////////////// +void CServer_Attach() +{ + DetourAttach((LPVOID*)&CServer_Authenticate, &HCServer_Authenticate); +} + +void CServer_Detach() +{ + DetourDetach((LPVOID*)&CServer_Authenticate, &HCServer_Authenticate); +} + +/////////////////////////////////////////////////////////////////////////////// +bool g_bCheckCompBanDB = true; \ No newline at end of file diff --git a/r5dev/server/server.h b/r5dev/server/server.h new file mode 100644 index 00000000..bd3c78d0 --- /dev/null +++ b/r5dev/server/server.h @@ -0,0 +1,53 @@ +#pragma once +#include "tier0/basetypes.h" +#include "networksystem/r5net.h" + +struct user_creds +{ + std::uint8_t gap0[16]; + std::uint32_t m_nIpAddr; + std::uint8_t gap1[4]; + std::int32_t m_nProtocolVer; + std::uint8_t gap2[12]; + std::int64_t m_nNucleusID; + std::int64_t m_nUserID; +}; + +namespace +{ + /* ==== CSERVER ========================================================================================================================================================= */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_CServer_Authenticate = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x44\x89\x44\x24\x00\x55\x56\x57\x48\x8D\xAC\x24\x00\x00\x00\x00", "xxxx?xxxxxxx????"); + void* (*CServer_Authenticate)(void* cserver, user_creds* creds) = (void* (*)(void* cserver, user_creds * creds))p_CServer_Authenticate.GetPtr(); /*44 89 44 24 ?? 55 56 57 48 8D AC 24 ?? ?? ?? ??*/ +#elif defined (GAMEDLL_S2) + ADDRESS p_CServer_Authenticate = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x44\x89\x44\x24\x00\x56\x57\x48\x81\xEC\x00\x00\x00\x00", "xxxx?xxxxx????"); + void* (*CServer_Authenticate)(void* cserver, user_creds* creds) = (void* (*)(void* cserver, user_creds * creds))p_CServer_Authenticate.GetPtr(); /*44 89 44 24 ?? 56 57 48 81 EC ?? ?? ?? ??*/ +#else + ADDRESS p_CServer_Authenticate = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x55\x57\x41\x55\x41\x57\x48\x8D\xAC\x24\x00\x00\x00\x00", "xxxxxxxxxxx????"); + void* (*CServer_Authenticate)(void* cserver, user_creds* creds) = (void* (*)(void* cserver, user_creds * creds))p_CServer_Authenticate.GetPtr(); /*40 55 57 41 55 41 57 48 8D AC 24 ?? ?? ?? ??*/ +#endif + ADDRESS p_CServer_RejectConnection = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x89\x4C\x24\x00\x53\x55\x56\x57\x48\x81\xEC\x00\x00\x00\x00\x49\x8B\xD9", "xxxx?xxxxxxx????xxx"); + void* (*CServer_RejectConnection)(void* pServer, unsigned int a2, user_creds* pCreds, const char* szMessage) = (void* (*)(void*, unsigned int, user_creds*, const char*))p_CServer_RejectConnection.GetPtr(); /*4C 89 4C 24 ?? 53 55 56 57 48 81 EC ?? ?? ?? ?? 49 8B D9*/ +} + +void CServer_Attach(); +void CServer_Detach(); + +void IsClientBanned(R5Net::Client* r5net, const std::string ipaddr, std::int64_t nucleus_id); +void* HCServer_Authenticate(void* cserver, user_creds* inpacket); + +extern bool g_bCheckCompBanDB; + +/////////////////////////////////////////////////////////////////////////////// +class HServer : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CServer::Authenticate : 0x" << std::hex << std::uppercase << p_CServer_Authenticate.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: CServer::RejectConnection : 0x" << std::hex << std::uppercase << p_CServer_RejectConnection.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HServer); diff --git a/r5dev/squirrel/sqapi.cpp b/r5dev/squirrel/sqapi.cpp new file mode 100644 index 00000000..4fa21a48 --- /dev/null +++ b/r5dev/squirrel/sqapi.cpp @@ -0,0 +1,79 @@ +//=============================================================================// +// +// Purpose: Squirrel API +// +//=============================================================================// + +#include "core/stdafx.h" +#include "squirrel/sqapi.h" + +char* hsq_getstring(void* sqvm, int i) +{ + std::uintptr_t thisptr = reinterpret_cast(sqvm); + + return *(char**)(*(std::int64_t*)(thisptr + 0x58) + 0x10 * i + 0x8) + 0x40; +} + +int hsq_getinteger(void* sqvm, int i) +{ + std::uintptr_t thisptr = reinterpret_cast(sqvm); + + return *(int*)(*(std::int64_t*)(thisptr + 0x58) + 0x10 * i + 0x8); +} + +void hsq_pushbool(void* sqvm, int val) +{ + sq_pushbool(sqvm, val); +} + +void hsq_pushstring(void* sqvm, const char* string, int len) +{ + sq_pushstring(sqvm, const_cast(string), len); +} + +void hsq_pushinteger(void* sqvm, int val) +{ + sq_pushinteger(sqvm, val); +} + +void hsq_newarray(void* sqvm, int size) +{ + sq_newarray(sqvm, size); +} + +void hsq_arrayappend(void* sqvm, int idx) +{ + sq_arrayappend(sqvm, idx); +} + +void hsq_newtable(void* sqvm) +{ + sq_newtable(sqvm); +} + +void hsq_newslot(void* sqvm, int idx) +{ + sq_newslot(sqvm, idx); +} + +void SQAPI_Attach() +{ + DetourAttach((LPVOID*)&sq_pushbool, &hsq_pushbool); + DetourAttach((LPVOID*)&sq_pushstring, &hsq_pushstring); + DetourAttach((LPVOID*)&sq_pushinteger, &hsq_pushinteger); + DetourAttach((LPVOID*)&sq_newarray, &hsq_newarray); + DetourAttach((LPVOID*)&sq_arrayappend, &hsq_arrayappend); + DetourAttach((LPVOID*)&sq_newtable, &hsq_newtable); + DetourAttach((LPVOID*)&sq_newslot, &hsq_newslot); +} + +void SQAPI_Detach() +{ + DetourDetach((LPVOID*)&sq_pushbool, &hsq_pushbool); + DetourDetach((LPVOID*)&sq_pushstring, &hsq_pushstring); + DetourDetach((LPVOID*)&sq_pushinteger, &hsq_pushinteger); + DetourDetach((LPVOID*)&sq_newarray, &hsq_newarray); + DetourDetach((LPVOID*)&sq_arrayappend, &hsq_arrayappend); + DetourDetach((LPVOID*)&sq_newtable, &hsq_newtable); + DetourDetach((LPVOID*)&sq_newslot, &hsq_newslot); +} \ No newline at end of file diff --git a/r5dev/squirrel/sqapi.h b/r5dev/squirrel/sqapi.h new file mode 100644 index 00000000..8dce4f0d --- /dev/null +++ b/r5dev/squirrel/sqapi.h @@ -0,0 +1,68 @@ +#pragma once +#include "tier0/basetypes.h" + +namespace +{ + /* ==== SQUIRREL ======================================================================================================================================================== */ + ADDRESS p_sq_pushbool = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x38\x33\xC0\x48\xC7\x44\x24\x20\x08\x00\x00\x01\x48", "xxxxxxxxxxxxxxxx"); + void (*sq_pushbool)(void* sqvm, int val) = (void (*)(void*, int))p_sq_pushbool.GetPtr(); /*48 83 EC 38 33 C0 48 C7 44 24 20 08 00 00 01 48*/ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) + ADDRESS p_sq_pushstring = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x56\x48\x83\xEC\x30\x48\x8B\xF1\x48\x85\xD2\x0F\x84\x8C\x00", "xxxxxxxxxxxxxxxx"); + void (*sq_pushstring)(void* sqvm, const char* string, int len) = (void (*)(void*, const char*, int))p_sq_pushstring.GetPtr(); /*40 56 48 83 EC 30 48 8B F1 48 85 D2 0F 84 8C 00*/ +#elif defined (GAMEDLL_S3) + ADDRESS p_sq_pushstring = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x56\x48\x83\xEC\x30\x48\x8B\xF1\x48\x85\xD2\x0F\x84\x8F\x00", "xxxxxxxxxxxxxxxx"); + void (*sq_pushstring)(void* sqvm, const char* string, int len) = (void (*)(void*, const char*, int))p_sq_pushstring.GetPtr(); /*40 56 48 83 EC 30 48 8B F1 48 85 D2 0F 84 8F 00*/ +#endif + ADDRESS p_sq_pushinteger = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x38\x33\xC0\x48\xC7\x44\x24\x20\x02\x00\x00\x05\x48", "xxxxxxxxxxxxxxxx"); + void (*sq_pushinteger)(void* sqvm, int val) = (void (*)(void*, int))p_sq_pushinteger.GetPtr(); /*48 83 EC 38 33 C0 48 C7 44 24 20 02 00 00 05 48*/ + + ADDRESS p_sq_newarray = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x57\x48\x83\xEC\x30\x48\x8B\xD9\x48\xC7\x44\x24\x20\x40", "xxxxxxxxxxxxxxxxxxx"); + void (*sq_newarray)(void* sqvm, int size) = (void (*)(void*, int))p_sq_newarray.GetPtr(); /*48 89 5C 24 08 57 48 83 EC 30 48 8B D9 48 C7 44 24 20 40*/ + + ADDRESS p_sq_arrayappend = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x8B\x41\x00\x48\x8B\xD9\x2B\x41\x00\x83\xF8\x02\x7D", "xxxxxxxx?xxxxx?xxxx"); + void (*sq_arrayappend)(void* sqvm, int idx) = (void (*)(void*, int))p_sq_arrayappend.GetPtr(); /*40 53 48 83 EC 20 8B 41 ?? 48 8B D9 2B 41 ?? 83 F8 02 7D*/ + + ADDRESS p_sq_newtable = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x08\x57\x48\x83\xEC\x30\x48\x8B\xD9\x48\xC7\x44\x24\x20\x20", "xxxxxxxxxxxxxxxxxxx"); + void (*sq_newtable)(void* sqvm) = (void (*)(void*))p_sq_newtable.GetPtr(); /*48 89 5C 24 08 57 48 83 EC 30 48 8B D9 48 C7 44 24 20 20*/ + + ADDRESS p_sq_newslot = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x30\x44\x8B\x49\x00\x48\x8B\xD9\x41\x8B\xC1", "xxxxxxxxx?xxxxxx"); + void (*sq_newslot)(void* sqvm, int idx) = (void (*)(void*, int))p_sq_newslot.GetPtr(); /*40 53 48 83 EC 20 8B 41 ?? 48 8B D9 2B 41 ?? 83 F8 02 7D*/ +} + +/////////////////////////////////////////////////////////////////////////////// +char* hsq_getstring(void* sqvm, int i); +int hsq_getinteger(void* sqvm, int i); + +void hsq_pushbool(void* sqvm, int val); + +void hsq_pushstring(void* sqvm, const char* string, int len); + +void hsq_pushinteger(void* sqvm, int val); + +void hsq_newarray(void* sqvm, int size); +void hsq_arrayappend(void* sqvm, int idx); + +void hsq_newtable(void* sqvm); +void hsq_newslot(void* sqvm, int idx); + +void SQAPI_Attach(); +void SQAPI_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HSqapi : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: sq_pushbool : 0x" << std::hex << std::uppercase << p_sq_pushbool.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: sq_pushstring : 0x" << std::hex << std::uppercase << p_sq_pushstring.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: sq_pushinteger : 0x" << std::hex << std::uppercase << p_sq_pushinteger.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: sq_newarray : 0x" << std::hex << std::uppercase << p_sq_newarray.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: sq_arrayappend : 0x" << std::hex << std::uppercase << p_sq_arrayappend.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: sq_newtable : 0x" << std::hex << std::uppercase << p_sq_newtable.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: sq_newslot : 0x" << std::hex << std::uppercase << p_sq_newslot.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HSqapi); diff --git a/r5dev/squirrel/sqinit.cpp b/r5dev/squirrel/sqinit.cpp new file mode 100644 index 00000000..7248ee24 --- /dev/null +++ b/r5dev/squirrel/sqinit.cpp @@ -0,0 +1,8 @@ +//=============================================================================// +// +// Create new functions here +// +//=============================================================================// + +#include "core/stdafx.h" +#include "sqinit.h" diff --git a/r5dev/squirrel/sqinit.h b/r5dev/squirrel/sqinit.h new file mode 100644 index 00000000..98e790f6 --- /dev/null +++ b/r5dev/squirrel/sqinit.h @@ -0,0 +1,23 @@ +#pragma once + +namespace +{ + ADDRESS p_Script_Remote_BeginRegisteringFunctions = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x83\x3D\x00\x00\x00\x00\x00\x74\x10", "xxxxxx?????xx"); + void* Script_Remote_BeginRegisteringFunctions = (void*)p_Script_Remote_BeginRegisteringFunctions.GetPtr(); /*48 83 EC 28 83 3D ?? ?? ?? ?? ?? 74 10*/ + + std::uint32_t* g_nRemoteFunctionCallsChecksum = reinterpret_cast(p_Script_Remote_BeginRegisteringFunctions.FindPatternSelf("89 05", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).GetPtr()); +} + +/////////////////////////////////////////////////////////////////////////////// +class HSqInit : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: Remote_BeginRegisteringFunctions : 0x" << std::hex << std::uppercase << p_Script_Remote_BeginRegisteringFunctions.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_nRemoteFunctionCallsChecksum : 0x" << std::hex << std::uppercase << g_nRemoteFunctionCallsChecksum << std::setw(0) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HSqInit); diff --git a/r5dev/squirrel/sqvm.cpp b/r5dev/squirrel/sqvm.cpp new file mode 100644 index 00000000..4eb69115 --- /dev/null +++ b/r5dev/squirrel/sqvm.cpp @@ -0,0 +1,306 @@ +//=============================================================================// +// +// Purpose: Squirrel VM +// +//=============================================================================// + +#include "core/stdafx.h" +#include "core/logdef.h" +#include "tier0/basetypes.h" +#include "tier0/cvar.h" +#include "tier0/IConVar.h" +#include "engine/sys_utils.h" +#include "squirrel/sqvm.h" +#include "vgui/CEngineVGui.h" +#include "gameui/IConsole.h" + +//--------------------------------------------------------------------------------- +// Purpose: prints the output of each VM to the console +//--------------------------------------------------------------------------------- +void* HSQVM_PrintFunc(void* sqvm, char* fmt, ...) +{ +#ifdef GAMEDLL_S3 + int vmIdx = *(int*)((std::uintptr_t)sqvm + 0x18); +#else // TODO [ AMOS ]: nothing equal to 'rdx + 18h' exist in the vm pointers for anything below S3. + int vmIdx = 3; +#endif + static bool initialized = false; + + static char buf[1024]; + static std::string vmType[4] = { "Script(S):", "Script(C):", "Script(U):", "Script(X):" }; + + static auto iconsole = spdlog::stdout_logger_mt("sqvm_print_iconsole"); // in-game console. + static auto wconsole = spdlog::stdout_logger_mt("sqvm_print_wconsole"); // windows console. + static auto sqlogger = spdlog::basic_logger_mt("sqvm_print_logger", "platform\\logs\\sqvm_print.log"); // file logger. + + std::string vmStr = vmType[vmIdx].c_str(); + + g_spd_sqvm_p_oss.str(""); + g_spd_sqvm_p_oss.clear(); + + if (!initialized) + { + iconsole = std::make_shared("sqvm_print_ostream", g_spd_sqvm_p_ostream_sink); + iconsole->set_pattern("[%S.%e] %v"); + iconsole->set_level(spdlog::level::debug); + wconsole->set_pattern("[%S.%e] %v"); + wconsole->set_level(spdlog::level::debug); + sqlogger->set_pattern("[%S.%e] %v"); + sqlogger->set_level(spdlog::level::debug); + initialized = true; + } + + va_list args; + va_start(args, fmt); + + vsnprintf(buf, sizeof(buf), fmt, args); + + buf[sizeof(buf) - 1] = 0; + va_end(args); + + vmStr.append(buf); + + if (sq_showvmoutput->m_pParent->m_iValue > 0) + { + sqlogger->debug(vmStr); + } + if (sq_showvmoutput->m_pParent->m_iValue > 1) + { + iconsole->debug(vmStr); + wconsole->debug(vmStr); +#ifndef DEDICATED + std::string s = g_spd_sqvm_p_oss.str(); + const char* c = s.c_str(); + Items.push_back(Strdup((const char*)c)); +#endif // !DEDICATED + } +#ifndef DEDICATED + if (sq_showvmoutput->m_pParent->m_iValue > 2) + { + std::string s = g_spd_sqvm_p_oss.str(); + g_pLogSystem.AddLog((LogType_t)vmIdx, s); + } +#endif // !DEDICATED + return NULL; +} + +//--------------------------------------------------------------------------------- +// Purpose: prints the warning output of each VM to the console +//--------------------------------------------------------------------------------- +void* HSQVM_WarningFunc(void* sqvm, int a2, int a3, int* nStringSize, void** ppString) +{ + void* result = SQVM_WarningFunc(sqvm, a2, a3, nStringSize, ppString); + if (g_bSQVM_WarnFuncCalled) // Check if its SQVM_Warning calling. + { + return result; // If not return. + } + + static bool initialized = false; + static std::string vmType[4] = { "Script(S): WARNING: ", "Script(C): WARNING: ", "Script(U): WARNING: ", "Script(X): WARNING: " }; + + static auto iconsole = spdlog::stdout_logger_mt("sqvm_warn_iconsole"); // in-game console. + static auto wconsole = spdlog::stdout_logger_mt("sqvm_warn_wconsole"); // windows console. + static auto sqlogger = spdlog::basic_logger_mt("sqvm_warn_logger", "platform\\logs\\sqvm_warn.log"); // file logger. + +#ifdef GAMEDLL_S3 + int vmIdx = *(int*)((std::uintptr_t)sqvm + 0x18); +#else // TODO [ AMOS ]: nothing equal to 'rdx + 18h' exist in the vm pointers for anything below S3. + int vmIdx = 3; +#endif + std::string vmStr = vmType[vmIdx].c_str(); + + g_spd_sqvm_w_oss.str(""); + g_spd_sqvm_w_oss.clear(); + + if (!initialized) + { + iconsole = std::make_shared("sqvm_warn_ostream", g_spd_sqvm_p_ostream_sink); + iconsole->set_pattern("[%S.%e] %v\n"); + iconsole->set_level(spdlog::level::debug); + wconsole->set_pattern("[%S.%e] %v\n"); + wconsole->set_level(spdlog::level::debug); + sqlogger->set_pattern("[%S.%e] %v\n"); + sqlogger->set_level(spdlog::level::debug); + initialized = true; + } + + std::string stringConstructor((char*)*ppString, *nStringSize); // Get string from memory via std::string constructor. + vmStr.append(stringConstructor); + + std::string s = g_spd_sqvm_w_oss.str(); + const char* c = s.c_str(); + + if (sq_showvmwarning->m_pParent->m_iValue > 0) + { + sqlogger->debug(vmStr); // Emit to file. + } + if (sq_showvmwarning->m_pParent->m_iValue > 1) + { + iconsole->debug(vmStr); // Emit to in-game console. + wconsole->debug(vmStr); // Emit to windows console. +#ifndef DEDICATED + std::string s = g_spd_sqvm_w_oss.str(); + const char* c = s.c_str(); + Items.push_back(Strdup(c)); +#endif // !DEDICATED + } +#ifndef DEDICATED + if (sq_showvmwarning->m_pParent->m_iValue > 2) + { + g_pLogSystem.AddLog((LogType_t)vmIdx, s); + const char* c = s.c_str(); + Items.push_back(Strdup(c)); + } +#endif // !DEDICATED + g_bSQVM_WarnFuncCalled = false; + + return result; +} + +//--------------------------------------------------------------------------------- +// Purpose: +//--------------------------------------------------------------------------------- +void* HSQVM_WarningCmd(int a1, int a2) +{ + g_bSQVM_WarnFuncCalled = true; + return SQVM_WarningCmd(a1, a2); +} + +//--------------------------------------------------------------------------------- +// Purpose: loads the include file from the mods directory +//--------------------------------------------------------------------------------- +void* HSQVM_LoadRson(const char* szRsonName) +{ + char szFilePath[MAX_PATH] = { 0 }; + sprintf_s(szFilePath, MAX_PATH, "platform\\%s", szRsonName); + + // Flip forward slashes in filepath to windows-style backslash + for (int i = 0; i < strlen(szFilePath); i++) + { + if (szFilePath[i] == '/') + { + szFilePath[i] = '\\'; + } + } + + // Returns the new path if the rson exists on the disk + if (FileExists(szFilePath) && SQVM_LoadRson(szRsonName)) + { + if (sq_showrsonloading->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "\n"); + DevMsg(eDLL_T::ENGINE, "______________________________________________________________\n"); + DevMsg(eDLL_T::ENGINE, "] RSON_DISK --------------------------------------------------\n"); + DevMsg(eDLL_T::ENGINE, "] PATH: '%s'\n", szFilePath); + DevMsg(eDLL_T::ENGINE, "--------------------------------------------------------------\n"); + DevMsg(eDLL_T::ENGINE, "\n"); + } + return SQVM_LoadRson(szFilePath); + } + else + { + if (sq_showrsonloading->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "\n"); + DevMsg(eDLL_T::ENGINE, "______________________________________________________________\n"); + DevMsg(eDLL_T::ENGINE, "] RSON_VPK ---------------------------------------------------\n"); + DevMsg(eDLL_T::ENGINE, "] PATH: '%s'\n", szRsonName); + DevMsg(eDLL_T::ENGINE, "--------------------------------------------------------------\n"); + DevMsg(eDLL_T::ENGINE, "\n"); + } + } + return SQVM_LoadRson(szRsonName); +} + +//--------------------------------------------------------------------------------- +// Purpose: loads the script file from the mods directory +//--------------------------------------------------------------------------------- +bool HSQVM_LoadScript(void* sqvm, const char* szScriptPath, const char* szScriptName, int nFlag) +{ + char filepath[MAX_PATH] = { 0 }; + sprintf_s(filepath, MAX_PATH, "platform\\%s", szScriptPath); + + // Flip forward slashes in filepath to windows-style backslash + for (int i = 0; i < strlen(filepath); i++) + { + if (filepath[i] == '/') + { + filepath[i] = '\\'; + } + } + + if (sq_showscriptloading->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "Loading SQVM Script '%s'\n", filepath); + } + + // Returns true if the script exists on the disk + if (FileExists(filepath) && SQVM_LoadScript(sqvm, filepath, szScriptName, nFlag)) + { + return true; + } + + if (sq_showscriptloading->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::ENGINE, "FAILED. Try SP / VPK for '%s'\n", filepath); + } + + /////////////////////////////////////////////////////////////////////////////// + return SQVM_LoadScript(sqvm, szScriptPath, szScriptName, nFlag); +} + +void HSQVM_RegisterFunction(void* sqvm, const char* szName, const char* szHelpString, const char* szRetValType, const char* szArgTypes, void* pFunction) +{ + SQFuncRegistration* sqFunc = new SQFuncRegistration(); + + sqFunc->m_szScriptName = szName; + sqFunc->m_szNativeName = szName; + sqFunc->m_szHelpString = szHelpString; + sqFunc->m_szRetValType = szRetValType; + sqFunc->m_szArgTypes = szArgTypes; + sqFunc->m_pFunction = pFunction; + + SQVM_RegisterFunc(sqvm, sqFunc, 1); +} + +int HSQVM_NativeTest(void* sqvm) +{ + // Function code goes here. + return 1; +} + +void RegisterUIScriptFunctions(void* sqvm) +{ + HSQVM_RegisterFunction(sqvm, "UINativeTest", "native ui function", "void", "", &HSQVM_NativeTest); +} + +void RegisterClientScriptFunctions(void* sqvm) +{ + HSQVM_RegisterFunction(sqvm, "ClientNativeTest", "native client function", "void", "", &HSQVM_NativeTest); +} + +void RegisterServerScriptFunctions(void* sqvm) +{ + HSQVM_RegisterFunction(sqvm, "ServerNativeTest", "native server function", "void", "", &HSQVM_NativeTest); +} + +void SQVM_Attach() +{ + DetourAttach((LPVOID*)&SQVM_PrintFunc, &HSQVM_PrintFunc); + DetourAttach((LPVOID*)&SQVM_WarningFunc, &HSQVM_WarningFunc); + DetourAttach((LPVOID*)&SQVM_WarningCmd, &HSQVM_WarningCmd); + DetourAttach((LPVOID*)&SQVM_LoadRson, &HSQVM_LoadRson); + DetourAttach((LPVOID*)&SQVM_LoadScript, &HSQVM_LoadScript); +} + +void SQVM_Detach() +{ + DetourDetach((LPVOID*)&SQVM_PrintFunc, &HSQVM_PrintFunc); + DetourDetach((LPVOID*)&SQVM_WarningFunc, &HSQVM_WarningFunc); + DetourDetach((LPVOID*)&SQVM_WarningCmd, &HSQVM_WarningCmd); + DetourDetach((LPVOID*)&SQVM_LoadRson, &HSQVM_LoadRson); + DetourDetach((LPVOID*)&SQVM_LoadScript, &HSQVM_LoadScript); +} + +/////////////////////////////////////////////////////////////////////////////// +bool g_bSQVM_WarnFuncCalled = false; diff --git a/r5dev/squirrel/sqvm.h b/r5dev/squirrel/sqvm.h new file mode 100644 index 00000000..5016e8fe --- /dev/null +++ b/r5dev/squirrel/sqvm.h @@ -0,0 +1,85 @@ +#pragma once + +struct SQFuncRegistration +{ + const char* m_szScriptName; // 00 + const char* m_szNativeName; // 08 + const char* m_szHelpString; // 10 + const char* m_szRetValType; // 18 + const char* m_szArgTypes; // 20 + std::int16_t unk28; // 28 + std::int16_t padding1; // 2A + std::int32_t unk2c; // 2C + std::int64_t unk30; // 30 + std::int32_t unk38; // 38 + std::int32_t padding2; // 3C + std::int64_t unk40; // 40 + std::int64_t unk48; // 48 + std::int64_t unk50; // 50 + std::int32_t unk58; // 58 + std::int32_t padding3; // 5C + void* m_pFunction; // 60 + + SQFuncRegistration() + { + memset(this, 0, sizeof(SQFuncRegistration)); + this->padding2 = 6; + } +}; + +namespace +{ + /* ==== SQUIRREL ======================================================================================================================================================== */ + ADDRESS p_SQVM_PrintFunc = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\xC4\x48\x89\x50\x10\x4C\x89\x40\x18\x4C\x89\x48\x20\x53\x56\x57\x48\x81\xEC\x30\x08\x00\x00\x48\x8B\xDA\x48\x8D\x70\x18\x48\x8B\xF9\xE8\x00\x00\x00\xFF\x48\x89\x74\x24\x28\x48\x8D\x54\x24\x30\x33", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx???xxxxxxxxxxxx"); + void* SQVM_PrintFunc = (void*)p_SQVM_PrintFunc.GetPtr(); /*48 8B C4 48 89 50 10 4C 89 40 18 4C 89 48 20 53 56 57 48 81 EC 30 08 00 00 48 8B DA 48 8D 70 18 48 8B F9 E8 ?? ?? ?? FF 48 89 74 24 28 48 8D 54 24 30 33*/ + + ADDRESS p_SQVM_WarningFunc = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x89\x4C\x24\x20\x44\x89\x44\x24\x18\x89\x54\x24\x10\x53\x55\x56\x57\x41\x54\x41\x55\x41\x56\x41\x57\x48\x83\xEC\x00\x48\x8B", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx?xx"); + void* (*SQVM_WarningFunc)(void* sqvm, int a2, int a3, int* nStringSize, void** ppString) = (void* (*)(void*, int, int, int*, void**))p_SQVM_WarningFunc.GetPtr(); /*4C 89 4C 24 20 44 89 44 24 18 89 54 24 10 53 55 56 57 41 54 41 55 41 56 41 57 48 83 EC ?? 48 8B*/ + + ADDRESS p_SQVM_WarningCmd = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x30\x33\xDB\x48\x8D\x44\x24\x00\x4C\x8D\x4C\x24\x00", "xxxxxxxxxxxx?xxxx?"); + void* (*SQVM_WarningCmd)(int a1, int a2) = (void* (*)(int, int))p_SQVM_WarningCmd.GetPtr(); /*40 53 48 83 EC 30 33 DB 48 8D 44 24 ?? 4C 8D 4C 24 ??*/ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_SQVM_LoadScript = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x10\x48\x89\x74\x24\x18\x48\x89\x7C\x24\x20\x48\x89\x4C\x24\x08\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8D\x6C", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + bool (*SQVM_LoadScript)(void* sqvm, const char* szScriptPath, const char* szScriptName, int nFlag) = (bool (*)(void*, const char*, const char*, int))p_SQVM_LoadScript.GetPtr(); /*48 89 5C 24 10 48 89 74 24 18 48 89 7C 24 20 48 89 4C 24 08 55 41 54 41 55 41 56 41 57 48 8D 6C*/ +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_SQVM_LoadScript = FindPatternSIMD("r5apex.exe", (const unsigned char*)"\x48\x8B\xC4\x48\x89\x48\x08\x55\x41\x56\x48\x8D\x68", "xxxxxxxxxxxxx"); /*48 8B C4 48 89 48 08 55 41 56 48 8D 68*/ + bool (*SQVM_LoadScript)(void* sqvm, const char* szScriptPath, const char* szScriptName, int nFlag) = (bool (*)(void*, const char*, const char*, int))p_SQVM_LoadScript.GetPtr(); +#endif + ADDRESS p_SQVM_LoadRson = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x8B\xDC\x49\x89\x5B\x08\x57\x48\x81\xEC\xA0\x00\x00\x00\x33", "xxxxxxxxxxxxxxxx"); + void* (*SQVM_LoadRson)(const char* szRsonName) = (void* (*)(const char*))p_SQVM_LoadRson.GetPtr(); /*4C 8B DC 49 89 5B 08 57 48 81 EC A0 00 00 00 33*/ + + ADDRESS p_SQVM_RegisterFunc = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x38\x45\x0F\xB6\xC8", "xxxxxxxx"); /*48 83 EC 38 45 0F B6 C8*/ + void* (*SQVM_RegisterFunc)(void* sqvm, SQFuncRegistration* sqFunc, int a1) = (void* (*)(void*, SQFuncRegistration*, int))p_SQVM_RegisterFunc.GetPtr(); +} + +void* HSQVM_PrintFunc(void* sqvm, char* fmt, ...); +void* HSQVM_LoadRson(const char* szRsonName); +bool HSQVM_LoadScript(void* sqvm, const char* szScriptPath, const char* szScriptName, int nFlags); + +void HSQVM_RegisterFunction(void* sqvm, const char* szName, const char* szHelpString, const char* szRetValType, const char* szArgTypes, void* pFunction); +int HSQVM_NativeTest(void* sqvm); + + +void SQVM_Attach(); +void SQVM_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +extern bool g_bSQVM_WarnFuncCalled; + +/////////////////////////////////////////////////////////////////////////////// +class HSQVM : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: SQVM_PrintFunc : 0x" << std::hex << std::uppercase << p_SQVM_PrintFunc.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: SQVM_WarningFunc : 0x" << std::hex << std::uppercase << p_SQVM_WarningFunc.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: SQVM_WarningCmd : 0x" << std::hex << std::uppercase << p_SQVM_WarningCmd.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: SQVM_LoadScript : 0x" << std::hex << std::uppercase << p_SQVM_LoadScript.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: SQVM_LoadRson : 0x" << std::hex << std::uppercase << p_SQVM_LoadRson.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: SQVM_RegisterFunc : 0x" << std::hex << std::uppercase << p_SQVM_RegisterFunc.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HSQVM); diff --git a/r5dev/src/CCompanion.cpp b/r5dev/src/CCompanion.cpp deleted file mode 100644 index fbeb8970..00000000 --- a/r5dev/src/CCompanion.cpp +++ /dev/null @@ -1,679 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "id3dx.h" -#include "console.h" -#include "patterns.h" -#include "gameclasses.h" -#include "CCompanion.h" -#include "r5net.h" - -CCompanion* g_ServerBrowser = nullptr; -bool g_CheckCompBanDB = true; - -std::map mapArray = -{ - { "mp_rr_canyonlands_64k_x_64k", "King's Canyon Season 0" }, - { "mp_rr_desertlands_64k_x_64k", "World's Edge Season 3" }, - { "mp_rr_canyonlands_mu1", "King's Canyon Season 2" }, - { "mp_rr_canyonlands_mu1_night", "King's Canyon Season 2 After Dark" }, - { "mp_rr_desertlands_64k_x_64k_nx", "World's Edge Season 3 After Dark" }, - { "mp_lobby", "Lobby Season 3" }, - { "mp_rr_canyonlands_staging", "King's Canyon Firing Range" } -}; - -/*----------------------------------------------------------------------------- - * _ccompanion.cpp - *-----------------------------------------------------------------------------*/ -CCompanion::CCompanion() : MatchmakingServerStringBuffer("r5a-comp-sv.herokuapp.com"), r5net(new R5Net::Client("r5a-comp-sv.herokuapp.com")) -{ - memset(ServerConnStringBuffer, 0, sizeof(ServerConnStringBuffer)); - - std::string path = "stbsp"; - for (const auto& entry : std::filesystem::directory_iterator(path)) - { - std::string filename = entry.path().string(); - int slashPos = filename.rfind("\\", std::string::npos); - filename = filename.substr((INT8)slashPos + 1, std::string::npos); - filename = filename.substr(0, filename.size() - 6); - - auto it = mapArray.find(filename); // Find MapName in mapArray. - if (it != mapArray.end()) - { - MapsList.push_back(it->second); - } - else - { - MapsList.push_back(filename); - } - } - - // copy assignment kjek - MyServer.map = MapsList[0]; - - static std::thread HostingServerRequestThread([this]() - { - while (true) - { - UpdateHostingStatus(); - std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - } - }); - - HostingServerRequestThread.detach(); -} - -CCompanion::~CCompanion() -{ - delete r5net; -} - -void CCompanion::UpdateHostingStatus() -{ - if (!GameGlobals::HostState || !GameGlobals::Cvar) // Is HostState and Cvar valid? - return; - - HostingStatus = GameGlobals::HostState->m_bActiveGame ? EHostStatus::Hosting : EHostStatus::NotHosting; // Are we hosting a server? - - switch (HostingStatus) - { - case EHostStatus::NotHosting: - { - HostRequestMessage = ""; - HostRequestMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - break; - } - case EHostStatus::Hosting: - { - if (ServerVisibility == EServerVisibility::Offline) // Do we wanna broadcast server to the browser? - break; - - if (*reinterpret_cast(0x1656057E0) == NULL) // Check if script checksum is valid yet. - break; - - switch (ServerVisibility) - { - - case EServerVisibility::Hidden: - MyServer.hidden = true; - break; - case EServerVisibility::Public: - MyServer.hidden = false; - break; - default: - break; - } - - SendHostingPostRequest(); - break; - } - default: - break; - } -} - -void CCompanion::RefreshServerList() -{ - ServerList.clear(); - - static bool bThreadLocked = false; - - if (!bThreadLocked) - { - std::thread t([this]() - { - spdlog::debug("[+CCompanion+] Refreshing server list with string {}\n", MatchmakingServerStringBuffer); - bThreadLocked = true; - ServerList = r5net->GetServersList(ServerListMessage); - bThreadLocked = false; - }); - - t.detach(); - } -} - -void CCompanion::SendHostingPostRequest() -{ - HostToken = std::string(); - spdlog::debug("[+CCompanion+] Sending PostServerHost request now..\n"); - bool result = r5net->PostServerHost(HostRequestMessage,HostToken, - ServerListing{ - MyServer.name, - std::string(GameGlobals::HostState->m_levelName), - "", - GameGlobals::Cvar->FindVar("hostport")->m_pzsCurrentValue, - GameGlobals::Cvar->FindVar("mp_gamemode")->m_pzsCurrentValue, - MyServer.hidden, - std::to_string(*reinterpret_cast(0x1656057E0)), - std::string(), - addr_NetChan_EncKey - } - ); - - if (result) - { - HostRequestMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); - std::stringstream msg; - msg << "Broadcasting! "; - if (!HostToken.empty()) - { - msg << "Share the following token for people to connect: "; - } - HostRequestMessage = msg.str().c_str(); - } - else - { - HostRequestMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); - } -} - - -void CCompanion::CompMenu() -{ - ImGui::BeginTabBar("CompMenu"); - if (ImGui::TabItemButton("Server Browser")) - { - SetSection(ESection::ServerBrowser); - } - if (ImGui::TabItemButton("Host Server")) - { - SetSection(ESection::HostServer); - } - if (ImGui::TabItemButton("Settings")) - { - SetSection(ESection::Settings); - } - ImGui::EndTabBar(); -} - -void CCompanion::ServerBrowserSection() -{ - ImGui::BeginGroup(); - ServerBrowserFilter.Draw(); - ImGui::SameLine(); - if (ImGui::Button("Refresh List")) - { - RefreshServerList(); - } - ImGui::EndGroup(); - ImGui::TextColored(ImVec4(1.00f, 0.00f, 0.00f, 1.00f), ServerListMessage.c_str()); - ImGui::Separator(); - - const float FooterHeight = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); - ImGui::BeginChild("ServerListChild", { 0, -FooterHeight }, true, ImGuiWindowFlags_AlwaysVerticalScrollbar); - if (ImGui::BeginTable("##ServerBrowser_ServerList", 5, ImGuiTableFlags_Resizable)) - { - ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch, 20); - ImGui::TableSetupColumn("Map", ImGuiTableColumnFlags_WidthStretch, 25); - ImGui::TableSetupColumn("Port", ImGuiTableColumnFlags_WidthStretch, 5); - ImGui::TableSetupColumn("Playlist", ImGuiTableColumnFlags_WidthStretch, 5); - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 5); - ImGui::TableHeadersRow(); - - for (ServerListing& server : ServerList) - { - const char* name = server.name.c_str(); - const char* map = server.map.c_str(); - const char* port = server.port.c_str(); - const char* playlist = server.playlist.c_str(); - - if (ServerBrowserFilter.PassFilter(name) - || ServerBrowserFilter.PassFilter(map) - || ServerBrowserFilter.PassFilter(port)) - { - ImGui::TableNextColumn(); - ImGui::Text(name); - - ImGui::TableNextColumn(); - ImGui::Text(map); - - ImGui::TableNextColumn(); - ImGui::Text(port); - - ImGui::TableNextColumn(); - ImGui::Text(playlist); - - ImGui::TableNextColumn(); - std::string selectButtonText = "Connect##"; - selectButtonText += (server.name + server.ip + server.map); - - if (ImGui::Button(selectButtonText.c_str())) - { - ConnectToServer(server.ip, server.port, server.netchanEncryptionKey); - } - } - - } - ImGui::EndTable(); - } - ImGui::EndChild(); - - ImGui::Separator(); - - ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth() / 4); - { - ImGui::InputTextWithHint("##ServerBrowser_ServerConnString", "Enter IP address or \"localhost\"", ServerConnStringBuffer, IM_ARRAYSIZE(ServerConnStringBuffer)); - - ImGui::SameLine(); - ImGui::InputTextWithHint("##ServerBrowser_ServerEncKey", "Enter the encryption key", ServerEncKeyBuffer, IM_ARRAYSIZE(ServerEncKeyBuffer)); - ImGui::SameLine(); - - if (ImGui::Button("Connect##ServerBrowser_ConnectByIp", ImVec2(ImGui::GetWindowContentRegionWidth() / 4, 18.5))) - { - ConnectToServer(ServerConnStringBuffer, ServerEncKeyBuffer); - } - - ImGui::SameLine(); - - if (ImGui::Button("Private Servers##ServerBrowser_HiddenServersButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 4, 18.5))) - { - ImGui::OpenPopup("Connect to Private Server##HiddenServersConnectModal"); - } - HiddenServersModal(); - // TODO: Still not in a seperate class... - - } - ImGui::PopItemWidth(); -} - -void CCompanion::HiddenServersModal() -{ - bool modalOpen = true; - if (ImGui::BeginPopupModal("Connect to Private Server##HiddenServersConnectModal", &modalOpen)) - { - // I *WILL* move this in a separate class - - ImGui::SetWindowSize(ImVec2(400.f, 200.f), ImGuiCond_Always); - - /* When removing this and adding the resource instead. Please initialize the texture in the CCompanion class constructor. - Pixie*/ - static unsigned char lockedserver[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff, 0x40, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x28, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa8, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x47, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0x70, 0xff, 0xff, 0xff, 0x1b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x33, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x08, 0xff, 0xff, 0xff, 0x88, 0xff, 0xff, 0xff, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xff, 0xff, 0xff, 0x47, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x44, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x2c, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x14, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8b, 0xff, 0xff, 0xff, 0x0b, 0xff, 0xff, 0xff, 0x27, 0xff, 0xff, 0xff, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x70, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xbc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xec, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x43, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x33, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x0c, 0xff, 0xff, 0xff, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x78, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x6c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb4, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3, 0xff, 0xff, 0xff, 0x57, 0xff, 0xff, 0xff, 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa3, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xb8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x43, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x50, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x94, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x60, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x48, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc4, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0x0b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4c, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x1b, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xff, 0xff, 0xff, 0xdc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x2c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb8, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x27, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x54, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x13, 0xff, 0xff, 0xff, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa8, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa0, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0x0b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x74, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x54, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x5f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x64, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd3, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x1c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x50, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x0c, 0xff, 0xff, 0xff, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x7c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x44, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xec, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x17, 0xff, 0xff, 0xff, 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb7, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x90, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xab, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x2c, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x34, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x48, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x93, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x1c, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb4, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x37, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xc4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0x10, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x10, 0xff, 0xff, 0xff, 0xa4, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x63, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x0b, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd8, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x0c, 0xff, 0xff, 0xff, 0xe8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdc, 0xff, 0xff, 0xff, 0x03, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x64, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x27, 0xff, 0xff, 0xff, 0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x8c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0x1b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x43, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x24, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x0b, 0xff, 0xff, 0xff, 0x27, 0xff, 0xff, 0xff, 0x04, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x1b, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x43, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x97, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x33, 0xff, 0xff, 0xff, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xb0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x67, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x17, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xdc, 0xff, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x28, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x4f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x63, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x14, 0xff, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc4, 0xff, 0xff, 0xff, 0x0b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x43, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0xc7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa8, 0xff, 0xff, 0xff, 0x04, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x4f, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x73, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x2b, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x48, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0x53, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x0b, 0xff, 0xff, 0xff, 0xb7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x08, 0xff, 0xff, 0xff, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x60, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x0c, 0xff, 0xff, 0xff, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0x14, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x64, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x9b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x67, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x13, 0xff, 0xff, 0xff, 0xc8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0xff, 0xff, 0xff, 0x34, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x4c, 0xff, 0xff, 0xff, 0x8f, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0x2b, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x93, 0xff, 0xff, 0xff, 0xdc, 0xff, 0xff, 0xff, 0x18, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }; - // ^^^^ - // this definitely wont reach the final commit lmao - // Nope it will for now -Pixie - if (!ApexLockIcon) - { - bool ret = LoadTextureFromByteArray(lockedserver, ApexLockIconWidth, ApexLockIconHeight, &ApexLockIcon); // Load texture from byte array. - } - - ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); // Override the style color for child bg. - ImGui::BeginChild("##HiddenServersConnectModal_IconParent", ImVec2(ApexLockIconWidth, ApexLockIconHeight)); - { - ImGui::Image(ApexLockIcon, ImVec2(ApexLockIconWidth, ApexLockIconHeight)); // Display texture. - } - ImGui::EndChild(); - ImGui::PopStyleColor(); // Pop the override for the child bg. - - ImGui::SameLine(); - - ImGui::Text("Enter the following details to continue"); - - ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth()); // Override item width. - ImGui::InputTextWithHint("##HiddenServersConnectModal_TokenInput", "Token", &HiddenServerToken); - ImGui::PopItemWidth(); // Pop item width. - - ImGui::Dummy(ImVec2(ImGui::GetWindowContentRegionWidth(), 19.f)); // Place a dummy, basically making space inserting a blank element. - - ImGui::TextColored(HiddenServerMessageColor, HiddenServerRequestMessage.c_str()); - - ImGui::Separator(); - - if (ImGui::Button("Connect##HiddenServersConnectModal_ConnectButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2, 24))) - { - HiddenServerRequestMessage = ""; - ServerListing server; - bool result = r5net->GetServerByToken(server, HiddenServerRequestMessage, HiddenServerToken); // Send token connect request. - if (!server.name.empty()) - { - ConnectToServer(server.ip, server.port, server.netchanEncryptionKey); // Connect to the server - HiddenServerRequestMessage = "Found Server: " + server.name; - HiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); - ImGui::CloseCurrentPopup(); - } - else - { - HiddenServerRequestMessage = "Error: " + HiddenServerRequestMessage; - HiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); - } - } - - ImGui::SameLine(); - - if (ImGui::Button("Close##HiddenServersConnectModal_CloseButton", ImVec2(ImGui::GetWindowContentRegionWidth() / 2, 24))) - { - ImGui::CloseCurrentPopup(); - } - - ImGui::EndPopup(); - } -} - -void CCompanion::HostServerSection() -{ - static std::string ServerNameErr = ""; - static std::string ServerMap = std::string(); - - ImGui::InputTextWithHint("##ServerHost_ServerName", "Server Name (Required)", &MyServer.name); - ImGui::Spacing(); - - if (ImGui::BeginCombo("Playlist##ServerHost_PlaylistBox", MyServer.playlist.c_str())) - { - for (auto& item : GameGlobals::allPlaylists) - { - if (ImGui::Selectable(item.c_str(), item == MyServer.playlist)) - { - MyServer.playlist = item; - } - } - ImGui::EndCombo(); - } - - if (ImGui::BeginCombo("Map##ServerHost_MapListBox", MyServer.map.c_str())) - { - for (auto& item : MapsList) - { - if (ImGui::Selectable(item.c_str(), item == MyServer.map)) - { - MyServer.map = item; - ServerMap = item; - for (auto it = mapArray.begin(); it != mapArray.end(); ++it) - { - if (it->second.compare(MyServer.map) == NULL) - ServerMap = it->first; - } - } - } - ImGui::EndCombo(); - } - - // ImGui::Checkbox("Start as Dedicated Server (HACK)##ServerHost_DediCheckbox", &StartAsDedi); - ImGui::Checkbox("Load Global Ban List##ServerHost_CheckCompBanDBCheckbox", &g_CheckCompBanDB); - - ImGui::Spacing(); - ImGui::SameLine(); - ImGui::Text("Server Visiblity"); - - if (ImGui::SameLine(); ImGui::RadioButton("Offline##ServerHost_ServerChoice1", ServerVisibility == EServerVisibility::Offline)) - { - ServerVisibility = EServerVisibility::Offline; - } - if (ImGui::SameLine(); ImGui::RadioButton("Hidden##ServerHost_ServerChoice2", ServerVisibility == EServerVisibility::Hidden)) - { - ServerVisibility = EServerVisibility::Hidden; - } - if (ImGui::SameLine(); ImGui::RadioButton("Public##ServerHost_ServerChoice2", ServerVisibility == EServerVisibility::Public)) - { - ServerVisibility = EServerVisibility::Public; - } - - ImGui::Spacing(); - ImGui::Separator(); - - if (!GameGlobals::HostState->m_bActiveGame) - { - if (ImGui::Button("Start Server##ServerHost_StartServerButton", ImVec2(ImGui::GetWindowSize().x, 32))) - { - if (!MyServer.name.empty() && !MyServer.playlist.empty() && !ServerMap.empty()) - { - spdlog::debug("[+CCompanion+] Starting Server with name {}, map {} and playlist {}..\n", MyServer.name, ServerMap, MyServer.playlist); - ServerNameErr = std::string(); - UpdateHostingStatus(); - - /* - * Playlist gets parsed in two instances, first in LoadPlaylist all the neccessary values. - * Then when you would normally call launchplaylist which calls StartPlaylist it would cmd call mp_gamemode which parses the gamemode specific part of the playlist.. - */ - addr_LoadPlaylist(MyServer.playlist.c_str()); - std::stringstream cgmd; - cgmd << "mp_gamemode " << MyServer.playlist; - ProcessCommand(cgmd.str().c_str()); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Avoid race condition. - - std::stringstream cmd; - cmd << "map " << ServerMap; - ProcessCommand(cmd.str().c_str()); - } - else - { - ServerNameErr = "No Server Name assigned / Playlist assigned / Map didn't apply properly."; - } - } - } - - if (ImGui::Button("Force Start##ServerHost_ForceStart", ImVec2(ImGui::GetWindowSize().x, 32))) - { - ServerNameErr = std::string(); - if (!ServerMap.empty()) - { - strncpy_s(GameGlobals::HostState->m_levelName, ServerMap.c_str(), 64); // Copy new map into hoststate levelname. 64 is size of m_levelname. - GameGlobals::HostState->m_iNextState = HostStates_t::HS_NEW_GAME; // Force CHostState::FrameUpdate to start a server. - } - else - { - ServerNameErr = "Failed to force start. Map didn't apply properly."; - } - } - - ImGui::TextColored(ImVec4(1.00f, 0.00f, 0.00f, 1.00f), ServerNameErr.c_str()); - ImGui::TextColored(HostRequestMessageColor, HostRequestMessage.c_str()); - if (!HostToken.empty()) - { - ImGui::InputText("##ServerHost_HostToken", &HostToken, ImGuiInputTextFlags_ReadOnly); - } - - if (GameGlobals::HostState->m_bActiveGame) - { - if (ImGui::Button("Reload Scripts##ServerHost_ReloadServerButton", ImVec2(ImGui::GetWindowSize().x, 32))) - { - spdlog::debug("[+CCompanion+] Reloading scripts..\n"); - ProcessCommand("reparse_weapons"); - ProcessCommand("reload"); - } - - if (ImGui::Button("Change Level##ServerHost_ChangeLevel", ImVec2(ImGui::GetWindowSize().x, 32))) - { - if (!ServerMap.empty()) - { - spdlog::debug("[+CCompanion+] Changing level to {}..\n", ServerMap); - strncpy_s(GameGlobals::HostState->m_levelName, ServerMap.c_str(), 64); // Copy new map into hoststate levelname. 64 is size of m_levelname. - GameGlobals::HostState->m_iNextState = HostStates_t::HS_CHANGE_LEVEL_MP; // Force CHostState::FrameUpdate to change the level. - } - else - { - ServerNameErr = "Failed to change level. Map didn't apply properly."; - } - } - - if (ImGui::Button("Stop Server##ServerHost_StopServerButton", ImVec2(ImGui::GetWindowSize().x, 32))) - { - spdlog::debug("[+CCompanion+] Stopping server..\n"); - ProcessCommand("LeaveMatch"); // Use script callback instead. - GameGlobals::HostState->m_iNextState = HostStates_t::HS_GAME_SHUTDOWN; // Force CHostState::FrameUpdate to shutdown the server for dedicated. - } - } - else - { - if (ImGui::Button("Reload Playlist from Disk##ServerHost_ReloadPlaylist", ImVec2(ImGui::GetWindowSize().x, 32))) - { - spdlog::debug("[+CCompanion+] Reloading playlist..\n"); - addr_downloadPlaylists_Callback(); - GameGlobals::InitPlaylist(); // Re-Init playlist. - } - } -} - -void CCompanion::SettingsSection() -{ - // Matchmaking Server String - ImGui::InputTextWithHint("##MatchmakingServerString", "Matchmaking Server String", &MatchmakingServerStringBuffer); - if (ImGui::Button("Update Settings")) - { - if (r5net) - { - delete r5net; - r5net = new R5Net::Client(MatchmakingServerStringBuffer); - } - } - // Encryption Key - if (ImGui::Button("Regenerate Encryption Key##SettingsSection_RegenEncKey")) - { - RegenerateEncryptionKey(); - } - ImGui::InputText("Encryption Key##SettingsSection_EncKey", addr_NetChan_EncKey, ImGuiInputTextFlags_ReadOnly); -} - -void CCompanion::Draw(const char* title) -{ - if (!ThemeSet) - { - SetStyleVar(); - ThemeSet = true; - } - - ImGui::SetNextWindowSize(ImVec2(840, 600), ImGuiCond_FirstUseEver); - ImGui::SetWindowPos(ImVec2(-500, 50), ImGuiCond_FirstUseEver); - - ImGui::Begin(title, NULL, ImGuiWindowFlags_NoScrollbar); - { - CompMenu(); - - switch (CurrentSection) - { - case ESection::ServerBrowser: - ServerBrowserSection(); - break; - case ESection::HostServer: - HostServerSection(); - break; - case ESection::Settings: - SettingsSection(); - break; - default: - break; - } - } - ImGui::End(); -} - -void CCompanion::ProcessCommand(const char* command_line) -{ - spdlog::debug("[+CCompanion+] Processing command: {} creating thread now.\n", command_line); - std::thread t(&CCompanion::ExecCommand, this, command_line); - spdlog::debug("[+CCompanion+] Thread created.\n"); - t.detach(); - spdlog::debug("[+CCompanion+] Detached from Thread.\n"); - - // HACK: This is to avoid a race condition. - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - - // TODO: - //ScrollToBottom = true; -} - -void CCompanion::ExecCommand(const char* command_line) -{ - spdlog::debug("[+CCompanion+] Executing command {}\n", command_line); - addr_CommandExecute(NULL, command_line); -} - -void CCompanion::ConnectToServer(const std::string ip, const std::string port, const std::string encKey) -{ - if (!encKey.empty()) - { - ChangeEncryptionKeyTo(encKey); - } - - std::stringstream cmd; - cmd << "connect " << ip << ":" << port; - ProcessCommand(cmd.str().c_str()); -} - -void CCompanion::ConnectToServer(const std::string connString, const std::string encKey) -{ - if (!encKey.empty()) - { - ChangeEncryptionKeyTo(encKey); - } - - std::stringstream cmd; - cmd << "connect " << connString; - ProcessCommand(cmd.str().c_str()); -} - -void CCompanion::RegenerateEncryptionKey() -{ - spdlog::debug("[+CCompanion+] Regenerating Encryption Key..\n"); - BCRYPT_ALG_HANDLE hAlgorithm; - if (BCryptOpenAlgorithmProvider(&hAlgorithm, L"RNG", 0, 0) < 0) - { - spdlog::critical("[+CCompanion+] Failed to open rng algorithm\n"); - } - unsigned char pBuffer[0x10u]; - if (BCryptGenRandom(hAlgorithm, pBuffer, 0x10u, 0) < 0) - { - spdlog::critical("[+CCompanion+] Failed to generate random data\n"); - } - std::string fin = std::string(); - - for (int i = 0; i < 0x10u; i++) - { - fin += pBuffer[i]; - } - - fin = base64_encode(fin); - - addr_NetChan_SetEncKey(addr_NetChan_EncKeyPtr.GetPtr(), fin.c_str()); -} - -void CCompanion::ChangeEncryptionKeyTo(const std::string str) -{ - addr_NetChan_SetEncKey(addr_NetChan_EncKeyPtr.GetPtr(), str.c_str()); -} \ No newline at end of file diff --git a/r5dev/src/CGameConsole.cpp b/r5dev/src/CGameConsole.cpp deleted file mode 100644 index 1ae16a13..00000000 --- a/r5dev/src/CGameConsole.cpp +++ /dev/null @@ -1,403 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "id3dx.h" -#include "console.h" -#include "patterns.h" -#include "gameclasses.h" -#include "CGameConsole.h" - -CGameConsole* g_GameConsole = nullptr; -ImVector Items; - -/*----------------------------------------------------------------------------- - * _cgameconsole.cpp - *-----------------------------------------------------------------------------*/ - -CGameConsole::CGameConsole() -{ - ClearLog(); - memset(InputBuf, 0, sizeof(InputBuf)); - - HistoryPos = -1; - AutoScroll = true; - ScrollToBottom = false; - ThemeSet = false; - - Commands.push_back("HELP"); - Commands.push_back("HISTORY"); - Commands.push_back("CLEAR"); - Commands.push_back("CLASSIFY"); - - AddLog("[DEBUG] THREAD ID: %ld\n", g_dThreadId); -} - -CGameConsole::~CGameConsole() -{ - ClearLog(); - History.clear(); -} - -/////////////////////////////////////////////////////////////////////////// -// Draw -void CGameConsole::Draw(const char* title) -{ - bool CopyToClipboard = false; - static auto cgameconsoleConfig = &g_GuiConfig.CGameConsoleConfig; - static auto ccompanionConfig = &g_GuiConfig.CCompanionConfig; - - if (!ThemeSet) - { - SetStyleVar(); - ThemeSet = true; - } - - if (cgameconsoleConfig->autoClear && Items.Size > cgameconsoleConfig->autoClearLimit) // Check if Auto-Clear is enabled and if its above our limit. If yes then clear. - { - ClearLog(); - History.clear(); - } - - //ImGui::ShowStyleEditor(); - - ImGui::SetNextWindowSize(ImVec2(1000, 600), ImGuiCond_FirstUseEver); - ImGui::SetWindowPos(ImVec2(-1000, 50), ImGuiCond_FirstUseEver); - - ImGui::Begin(title, NULL); // ImGui::Begin should never fail, if it does we got another problem. - { - // Reserve enough left-over height and width for 1 separator + 1 input text - const float FooterHeightToReserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); - const float FooterWidthtoReserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetWindowWidth(); - - /////////////////////////////////////////////////////////////////////// - ImGui::Separator(); - if (ImGui::BeginPopup("Options")) - { - if (ImGui::Checkbox("Print to Cmd", &cgameconsoleConfig->printCmd)) - g_GuiConfig.Save(); - - ImGui::Checkbox("Auto-Scroll", &AutoScroll); - - if (ImGui::Checkbox("Auto-Clear", &cgameconsoleConfig->autoClear)) - g_GuiConfig.Save(); - - ImGui::SameLine(); - - ImGui::PushItemWidth(100); - if (ImGui::InputInt("Auto Clear Limit##AutoClearAfterCertainIndexCGameConsole", &cgameconsoleConfig->autoClearLimit)) - g_GuiConfig.Save(); - - ImGui::PopItemWidth(); - - if (ImGui::SmallButton("Clear")) - ClearLog(); - - ImGui::SameLine(); - CopyToClipboard = ImGui::SmallButton("Copy"); - ImGui::Text("CG Hotkey:"); - ImGui::SameLine(); - if (ImGui::Hotkey("##OpenCGameConsoleBind1", &cgameconsoleConfig->bind1, ImVec2(80, 80))) - g_GuiConfig.Save(); - - ImGui::Text("CC Hotkey:"); - ImGui::SameLine(); - - if (ImGui::Hotkey("##OpenCCompanionBind1", &ccompanionConfig->bind1, ImVec2(80, 80))) - g_GuiConfig.Save(); - - ImGui::EndPopup(); - } - if (ImGui::Button("Options")) - { - ImGui::OpenPopup("Options"); - } - ImGui::SameLine(); - if (ImGui::BeginPopup("Tools")) - { - Hooks::bToggledDevFlags ? ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0, 255, 0, 255)) : ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 0, 0, 255)); - if (ImGui::SmallButton("Developer Mode")) - { - Hooks::ToggleDevCommands(); - AddLog("+--------------------------------------------------------+\n"); - AddLog("|>>>>>>>>>>>>>>| DEVONLY COMMANDS TOGGLED |<<<<<<<<<<<<<<|\n"); - AddLog("+--------------------------------------------------------+\n"); - ProcessCommand("exec autoexec"); - } - ImGui::PopStyleColor(); // Pop color override. - Hooks::bToggledNetTrace ? ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0, 255, 0, 255)) : ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 0, 0, 255)); - if (ImGui::SmallButton("Netchannel Trace")) - { - Hooks::ToggleNetTrace(); - AddLog("+--------------------------------------------------------+\n"); - AddLog("|>>>>>>>>>>>>>>| NETCHANNEL TRACE TOGGLED |<<<<<<<<<<<<<<|\n"); - AddLog("+--------------------------------------------------------+\n"); - ProcessCommand("exec netchan"); - } - ImGui::PopStyleColor(); // Pop color override. - if (ImGui::SmallButton("Commands/Convars to Console")) - { - if (GameGlobals::Cvar) - { - for (auto map : GameGlobals::Cvar->DumpToMap()) - { - AddLog("%s\n", map.first.c_str()); - } - } - } - ImGui::EndPopup(); - } - if (ImGui::Button("Tools")) - { - ImGui::OpenPopup("Tools"); - } - ImGui::SameLine(); - Filter.Draw("Filter [\"-incl,-excl\"] [\"error\"]", FooterWidthtoReserve - 500); - ImGui::Separator(); - - /////////////////////////////////////////////////////////////////////// - ImGui::BeginChild("ScrollingRegion", ImVec2(0, -FooterHeightToReserve), true, ImGuiWindowFlags_AlwaysVerticalScrollbar); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 4.f, 6.f }); - if (CopyToClipboard) - { - ImGui::LogToClipboard(); - } - - for (int i = 0; i < Items.Size; i++) - { - const char* item = Items[i]; - if (!Filter.PassFilter(item)) - continue; - - /////////////////////////////////////////////////////////////////// - ImVec4 color; - bool has_color = false; - - /////////////////////////////////////////////////////////////////// - // General - if (strstr(item, "[INFO]")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); has_color = true; } - if (strstr(item, "[ERROR]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, "[DEBUG]")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } - if (strstr(item, "[WARNING]")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } - if (strncmp(item, "# ", 2) == 0) { color = ImVec4(1.00f, 0.80f, 0.60f, 1.00f); has_color = true; } - - /////////////////////////////////////////////////////////////////// - // Virtual machines - if (strstr(item, "Script(S):")) { color = ImVec4(0.59f, 0.58f, 0.73f, 1.00f); has_color = true; } - if (strstr(item, "Script(C):")) { color = ImVec4(0.59f, 0.58f, 0.63f, 1.00f); has_color = true; } - if (strstr(item, "Script(U):")) { color = ImVec4(0.59f, 0.48f, 0.53f, 1.00f); has_color = true; } - - if (strstr(item, "Script(S) Warning:")) { color = ImVec4(0.80f, 0.80f, 0.73f, 1.00f); has_color = true; } - if (strstr(item, "Script(C) Warning:")) { color = ImVec4(0.80f, 0.80f, 0.63f, 1.00f); has_color = true; } - if (strstr(item, "Script(U) Warning:")) { color = ImVec4(0.80f, 0.80f, 0.53f, 1.00f); has_color = true; } - - /////////////////////////////////////////////////////////////////// - // Callbacks - //if (strstr(item, "CodeCallback_")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } - - /////////////////////////////////////////////////////////////////// - // Script errors - if (strstr(item, ".gnut")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.60f); has_color = true; } - if (strstr(item, ".nut")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.60f); has_color = true; } - if (strstr(item, "[CLIENT]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, "[SERVER]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, "[UI]")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, "SCRIPT ERROR")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, "SCRIPT COMPILE")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, ".gnut #")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, ".nut #")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - if (strstr(item, "): -> ")) { color = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); has_color = true; } - - /////////////////////////////////////////////////////////////////// - // Script debug - if (strstr(item, "CALLSTACK")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } - if (strstr(item, "LOCALS")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } - if (strstr(item, "*FUNCTION")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } - if (strstr(item, "DIAGPRINTS")) { color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f); has_color = true; } - if (strstr(item, " File : ")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } - if (strstr(item, "<><>GRX<><>")) { color = ImVec4(0.00f, 0.30f, 1.00f, 1.00f); has_color = true; } - - /////////////////////////////////////////////////////////////////// - // Filters - //if (strstr(item, ") -> ")) { color = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); has_color = true; } - - if (has_color) - { - ImGui::PushStyleColor(ImGuiCol_Text, color); - } - - ImGui::TextWrapped(item); - - if (has_color) - { - ImGui::PopStyleColor(); - } - } - - if (CopyToClipboard) - { - ImGui::LogFinish(); - } - - if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) - { - ImGui::SetScrollHereY(1.0f); - } - ScrollToBottom = false; - - /////////////////////////////////////////////////////////////////////// - ImGui::PopStyleVar(); - ImGui::EndChild(); - ImGui::Separator(); - - /////////////////////////////////////////////////////////////////////// - // Console - bool ShouldReclaimFocus = false; - ImGui::PushItemWidth(FooterWidthtoReserve - 80); - if (ImGui::IsWindowAppearing()) { ImGui::SetKeyboardFocusHere(); } - ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory; - - std::function CommandExec = [&](char* InputBuf) - { - char* s = InputBuf; - const char* replace = ""; - if (strstr(InputBuf, "`")) - { - strcpy_s(s, sizeof(replace), replace); - } - - Strtrim(s); - - if (s[0]) - { - ProcessCommand(s); - } - - strcpy_s(s, sizeof(replace), replace); - ShouldReclaimFocus = true; - }; - - if (ImGui::InputText("##input", InputBuf, IM_ARRAYSIZE(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this)) - { - CommandExec(InputBuf); - } - - ImGui::SameLine(); - - if (ImGui::Button("Submit")) - { - CommandExec(InputBuf); - } - - // Auto-focus on window apparition - ImGui::SetItemDefaultFocus(); - - // Auto focus previous widget - if (ShouldReclaimFocus) - { - ImGui::SetKeyboardFocusHere(-1); - } - } - ImGui::End(); -} - -/////////////////////////////////////////////////////////////////////////// -// Exec -void CGameConsole::ProcessCommand(const char* command_line) -{ - std::thread t(&CGameConsole::ExecCommand, this, command_line); - t.detach(); - - // HACK: This is to avoid a race condition. - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - AddLog("# %s\n", command_line); - - HistoryPos = -1; - for (int i = History.Size - 1; i >= 0; i--) - { - if (Stricmp(History[i], command_line) == 0) - { - delete History[i]; - History.erase(History.begin() + i); - break; - } - } - - History.push_back(Strdup(command_line)); - if (Stricmp(command_line, "CLEAR") == 0) - { - ClearLog(); - } - else if (Stricmp(command_line, "HELP") == 0) - { - AddLog("Commands:"); - for (int i = 0; i < Commands.Size; i++) - { - AddLog("- %s", Commands[i]); - } - } - else if (Stricmp(command_line, "HISTORY") == 0) - { - int first = History.Size - 10; - for (int i = first > 0 ? first : 0; i < History.Size; i++) - { - AddLog("%3d: %s\n", i, History[i]); - } - } - - ScrollToBottom = true; -} - -void CGameConsole::ExecCommand(const char* command_line) -{ - addr_CommandExecute(NULL, command_line); -} - -/////////////////////////////////////////////////////////////////////////// -// Edit -int CGameConsole::TextEditCallback(ImGuiInputTextCallbackData* data) -{ - switch (data->EventFlag) - { - case ImGuiInputTextFlags_CallbackCompletion: - { - // Locate beginning of current word - const char* word_end = data->Buf + data->CursorPos; - const char* word_start = word_end; - while (word_start > data->Buf) - { - const char c = word_start[-1]; - if (c == ' ' || c == '\t' || c == ',' || c == ';') - { - break; - } - word_start--; - } - break; - } - case ImGuiInputTextFlags_CallbackHistory: - { - const int prev_history_pos = HistoryPos; - if (data->EventKey == ImGuiKey_UpArrow) - { - if (HistoryPos == -1) { HistoryPos = History.Size - 1; } - else if (HistoryPos > 0) { HistoryPos--; } - } - else if (data->EventKey == ImGuiKey_DownArrow) - { - if (HistoryPos != -1) - { - if (++HistoryPos >= History.Size) - { - HistoryPos = -1; - } - } - } - if (prev_history_pos != HistoryPos) - { - const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : ""; - data->DeleteChars(0, data->BufTextLen); - data->InsertChars(0, history_str); - } - } - } - return 0; -} \ No newline at end of file diff --git a/r5dev/src/cenginevgui.cpp b/r5dev/src/cenginevgui.cpp deleted file mode 100644 index cec33ebe..00000000 --- a/r5dev/src/cenginevgui.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "logsystem.h" - -namespace Hooks -{ - CEngineVGui_PaintFn originalCEngineVGui_Paint = nullptr; -} - -int Hooks::CEngineVGui_Paint(void* thisptr, int mode) -{ - int result = originalCEngineVGui_Paint(thisptr, mode); - - static void* pCMatSystemSurface = MemoryAddress(0x14D40B3B0).RCast(); - static auto fnRenderStart = MemoryAddress(0x14053EFC0).RCast(); - static auto fnRenderEnd = MemoryAddress(0x14053F1B0).RCast(); - - if (mode == 1 || mode == 2) // Render in main menu and ingame. - { - fnRenderStart(pCMatSystemSurface); - - g_LogSystem.Update(); - - fnRenderEnd(); - } - - return result; -} \ No newline at end of file diff --git a/r5dev/src/gameclasses.cpp b/r5dev/src/gameclasses.cpp deleted file mode 100644 index 5c4ae395..00000000 --- a/r5dev/src/gameclasses.cpp +++ /dev/null @@ -1,532 +0,0 @@ -#include "pch.h" -#include "gameclasses.h" -#include "id3dx.h" -#include "cgameconsole.h" -#include "squirrel.h" -#include - -// Need this for a re-factor later. -// Interface* interfaces = *reinterpret_cast(0x167F4FA48); - -// for (Interface* current = interfaces; current; current = reinterpret_cast(current->NextInterfacePtr)) -// { -// printf("%s: %p\n", current->InterfaceName, current->InterfacePtr); -// } - -namespace GameGlobals -{ - bool IsInitialized = false; - CHostState* HostState = nullptr; - CInputSystem* InputSystem = nullptr; - CCVar* Cvar = nullptr; - CClient* Client = nullptr; - BanList* BanSystem = new BanList(); - - CKeyValuesSystem* KeyValuesSystem = nullptr; - KeyValues** PlaylistKeyValues = nullptr; - - std::vector allPlaylists = { "none" }; - - namespace CustomCommandVariations - { - void CGameConsole_Callback(const CCommand& cmd) - { - g_bShowConsole = !g_bShowConsole; - } - - void CCompanion_Callback(const CCommand& cmd) - { - g_bShowBrowser = !g_bShowBrowser; - } - - void Kick_Callback(CCommand* cmd) - { - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - const char* firstArg = cmdReference[1]; // Get first arg. - - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. - std::string clientName((char*)clientNamePtr, 32); // Get full name. - - if (clientName.empty()) // Empty name? - continue; - - if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? - continue; - - DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. - } - } - - void KickID_Callback(CCommand* cmd) - { - static auto HasOnlyDigits = [](const std::string& string) - { - for (const char& character : string) - { - if (std::isdigit(character) == 0) - return false; - } - return true; - }; - - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - std::string firstArg = cmdReference[1]; // Get first arg. - - try - { - bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. - MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. - if (ipAddressField) - { - std::stringstream ss; - ss << std::to_string(ipAddressField.GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x3).GetValue()); - - finalIPAddress = ss.str(); - } - - if (onlyDigits) - { - std::int64_t ID = static_cast(std::stoll(firstArg)); - if (ID > MAX_PLAYERS) // Is it a possible originID? - { - std::int64_t originID = client->m_iOriginID; - if (originID != ID) // See if they match. - continue; - } - else // If its not try by userID. - { - std::int64_t clientID = static_cast(client->m_iUserID + 1); // Get UserID + 1. - if (clientID != ID) // See if they match. - continue; - } - - DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. - } - else - { - if (firstArg.compare(finalIPAddress) != NULL) // Do the string equal? - continue; - - DisconnectClient(client, "Kicked from Server", 0, 1); // Disconnect client. - } - } - } - catch (std::exception& e) - { - spdlog::critical("Kick UID asked for a userID or originID :( You can get the userid with the 'status' command. Error: {}\n", e.what()); - g_GameConsole->AddLog("Kick UID asked for a userID or originID :( You can get the userid with the 'status' command. Error: %s", e.what()); - return; - } - } - - void Unban_Callback(CCommand* cmd) - { - static auto HasOnlyDigits = [](const std::string& string) - { - for (const char& character : string) - { - if (std::isdigit(character) == 0) - return false; - } - return true; - }; - - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - - try - { - const char* firstArg = cmdReference[1]; - if (HasOnlyDigits(firstArg)) // Check if we have an ip address or origin ID. - { - GameGlobals::BanSystem->DeleteEntry("noIP", std::stoll(firstArg)); // Delete ban entry. - GameGlobals::BanSystem->Save(); // Save modified vector to file. - } - else - { - GameGlobals::BanSystem->DeleteEntry(firstArg, 1); // Delete ban entry. - GameGlobals::BanSystem->Save(); // Save modified vector to file. - } - } - catch (std::exception& e) - { - spdlog::critical("Unban Error: {}\n", e.what()); - g_GameConsole->AddLog("Unban Error: %s", e.what()); - return; - } - } - - void ReloadBanList_Callback(CCommand* cmd) - { - GameGlobals::BanSystem->Load(); // Reload banlist. - } - - void Ban_Callback(CCommand* cmd) - { - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - const char* firstArg = cmdReference[1]; // Get first arg. - - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. - std::string clientName((char*)clientNamePtr, 32); // Get full name. - - if (clientName.empty()) // Empty name? - continue; - - if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? - continue; - - std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. - MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. - if (ipAddressField && ipAddressField.GetValue() != 0x0) - { - std::stringstream ss; - ss << std::to_string(ipAddressField.GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x3).GetValue()); - - finalIPAddress = ss.str(); - } - - GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. - GameGlobals::BanSystem->Save(); // Save ban list. - DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. - } - } - - void BanID_Callback(CCommand* cmd) - { - static auto HasOnlyDigits = [](const std::string& string) - { - for (const char& character : string) - { - if (std::isdigit(character) == 0) - return false; - } - return true; - }; - - std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); - if (argSize < 2) // Do we atleast have 2 arguments? - return; - - CCommand& cmdReference = *cmd; // Get reference. - std::string firstArg = cmdReference[1]; - - try - { - bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - std::string finalIPAddress = "null"; // If this stays null they modified the packet somehow. - MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. - if (ipAddressField) - { - std::stringstream ss; - ss << std::to_string(ipAddressField.GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x3).GetValue()); - - finalIPAddress = ss.str(); - } - - if (onlyDigits) - { - std::int64_t ID = static_cast(std::stoll(firstArg)); - if (ID > MAX_PLAYERS) // Is it a possible originID? - { - std::int64_t originID = client->m_iOriginID; - if (originID != ID) // See if they match. - continue; - } - else // If its not try by userID. - { - std::int64_t clientID = static_cast(client->m_iUserID + 1); // Get UserID + 1. - if (clientID != ID) // See if they match. - continue; - } - - GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. - GameGlobals::BanSystem->Save(); // Save ban list. - DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. - } - else - { - if (firstArg.compare(finalIPAddress) != NULL) // Do the string equal? - continue; - - GameGlobals::BanSystem->AddEntry(finalIPAddress, client->m_iOriginID); // Add ban entry. - GameGlobals::BanSystem->Save(); // Save ban list. - DisconnectClient(client, "Banned from Server", 0, 1); // Disconnect client. - } - } - } - catch (std::exception& e) - { - spdlog::critical("Banid Error: {}\n", e.what()); - g_GameConsole->AddLog("Banid Error: %s", e.what()); - return; - } - } - } - - void NullHostNames() - { - spdlog::debug("Nulling host names..\n"); - const char* hostnameArray[] = - { - "pin_telemetry_hostname", - "assetdownloads_hostname", - "users_hostname", - "persistence_hostname", - "speechtotexttoken_hostname", - "communities_hostname", - "persistenceDef_hostname", - "party_hostname", - "speechtotext_hostname", - "serverReports_hostname", - "subscription_hostname", - "steamlink_hostname", - "staticfile_hostname", - "matchmaking_hostname", - "skill_hostname", - "publication_hostname", - "stats_hostname" - }; - - for (int i = 0; i < 17; i++) - { - const char* name = hostnameArray[i]; - Cvar->FindVar(name)->m_pzsCurrentValue = "0.0.0.0"; - } - } - - void InitGameGlobals() - { - spdlog::debug("Initializing Game Globals..\n"); - HostState = reinterpret_cast(0x141736120); // Get CHostState from memory. - InputSystem = *reinterpret_cast(0x14D40B380); // Get IInputSystem from memory. - Cvar = *reinterpret_cast(0x14D40B348); // Get CCVar from memory. - KeyValuesSystem = reinterpret_cast(0x141F105C0); // Get CKeyValuesSystem from memory. - PlaylistKeyValues = reinterpret_cast(0x16705B980); // Get the KeyValue for the playlist file. - Client = reinterpret_cast(0x16073B200); - - NullHostNames(); // Null all hostnames. - InitAllCommandVariations(); // Initialize our custom ConVars. - *(char*)addr_m_bRestrictServerCommands = true; // Restrict commands. - void* disconnect = Cvar->FindCommand("disconnect"); - *(std::int32_t*)((std::uintptr_t)disconnect + 0x38) |= FCVAR_SERVER_CAN_EXECUTE; // Make sure server is not restricted to this. - - std::thread t1(InitPlaylist); // Start thread to grab playlists. - t1.detach(); // Detach thread from current one. - - IsInitialized = true; - } - - void InitPlaylist() - { - spdlog::debug("Parsing Playlist..\n"); - while (true) - { - if ((*PlaylistKeyValues)) - { - KeyValues* playlists = (*PlaylistKeyValues)->FindKey("Playlists", false); // Find playlists key. - if (playlists) - { - allPlaylists.clear(); - for (KeyValues* dat = playlists->m_pSub; dat != nullptr; dat = dat->m_pPeer) // Parse through all sub keys. - { - allPlaylists.push_back(dat->GetName()); // Get all playlist names. - } - - break; // Break if playlist got filled. - } - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - } - - void InitAllCommandVariations() - { - spdlog::debug("Initializing all Custom ConVars and Commands..\n"); - void* CGameConsoleConCommand = CreateCustomConCommand("cgameconsole", "Opens the R5 Reloaded Console.", 0, CustomCommandVariations::CGameConsole_Callback, nullptr); - void* CCompanionConCommand = CreateCustomConCommand("ccompanion", "Opens the R5 Reloaded Server Browser.", 0, CustomCommandVariations::CCompanion_Callback, nullptr); - void* KickConCommand = CreateCustomConCommand("kick", "Kick a client from the Server via name. | Usage: kick (name).", 0, CustomCommandVariations::Kick_Callback, nullptr); - void* KickIDConCommand = CreateCustomConCommand("kickid", "Kick a client from the Server via userID or originID | Usage: kickid (originID/userID)", 0, CustomCommandVariations::KickID_Callback, nullptr); - void* UnbanConCommand = CreateCustomConCommand("unban", "Unbans a client from the Server via IP or originID | Usage: unban (originID/ipAddress)", 0, CustomCommandVariations::Unban_Callback, nullptr); - void* ReloadBanListConCommand = CreateCustomConCommand("reloadbanlist", "Reloads the ban list from disk.", 0, CustomCommandVariations::ReloadBanList_Callback, nullptr); - void* BanConCommand = CreateCustomConCommand("ban", "Bans a client from the Server via name. | Usage: ban (name)", 0, CustomCommandVariations::Ban_Callback, nullptr); - void* BanIDConCommand = CreateCustomConCommand("banid", "Bans a client from the Server via originID, userID or IP | Usage: banid (originID/ipAddress/userID)", 0, CustomCommandVariations::BanID_Callback, nullptr); - - ConVar* DrawConsoleOverlayConVar = CreateCustomConVar("cl_drawconsoleoverlay", "0", 0, "Draw the console overlay at the top of the screen", false, 0.f, false, 0.f, nullptr, nullptr); - ConVar* ConsoleOverlayLinesConVar = CreateCustomConVar("cl_consoleoverlay_lines", "3", 0, "Number of lines of console output to draw", false, 1.f, false, 50.f, nullptr, nullptr); - } - - void* CreateCustomConCommand(const char* name, const char* helpString, int flags, void* callback, void* callbackAfterExecution) - { - static MemoryAddress ConCommandVtable = MemoryAddress(0x14136BD70); - static MemoryAddress NullSub = MemoryAddress(0x1401B3280); - static MemoryAddress CallbackCompletion = MemoryAddress(0x1401E3990); - static MemoryAddress RegisterConCommand = MemoryAddress(0x14046F470); - - void* command = reinterpret_cast(addr_MemAlloc_Wrapper(0x68)); // Allocate new memory with StdMemAlloc else we crash. - memset(command, 0, 0x68); // Set all to null. - std::uintptr_t commandPtr = reinterpret_cast(command); // To ptr. - - *(void**)commandPtr = ConCommandVtable.RCast(); // 0x0 to ConCommand vtable. - *(const char**)(commandPtr + 0x18) = name; // 0x18 to ConCommand Name. - *(const char**)(commandPtr + 0x20) = helpString; // 0x20 to ConCommand help string. - *(std::int32_t*)(commandPtr + 0x38) = flags; // 0x38 to ConCommand Flags. - *(void**)(commandPtr + 0x40) = NullSub.RCast(); // 0x40 Nullsub since every concommand has it. - *(void**)(commandPtr + 0x50) = callback; // 0x50 has function callback. - *(std::int32_t*)(commandPtr + 0x60) = 2; // 0x60 Set to use callback and newcommand callback. - - if (callbackAfterExecution) // Do we wanna have a callback after execution? - { - *(void**)(commandPtr + 0x58) = callbackAfterExecution; // 0x58 to our callback after execution. - } - else - { - *(void**)(commandPtr + 0x58) = CallbackCompletion.RCast(); // 0x58 nullsub. - } - - RegisterConCommand.RCast()((void*)commandPtr); // Register command in ConVarAccessor. - - return command; - } - - ConVar* CreateCustomConVar(const char* name, const char* defaultValue, int flags, const char* helpString, bool bMin, float fMin, bool bMax, float fMax, void* callback, void* unk) - { - static MemoryAddress ConVarVtable = MemoryAddress(0x14046FB50).Offset(0x12).ResolveRelativeAddress(); // Get vtable ptr for ConVar table. - static MemoryAddress ICvarVtable = MemoryAddress(0x14046FB50).Offset(0x29).ResolveRelativeAddress(); // Get vtable ptr for ICvar table. - static MemoryAddress CreateConVar = MemoryAddress(0x140470540); // Get CreateConvar address. - - ConVar* allocatedConvar = reinterpret_cast(addr_MemAlloc_Wrapper(0xA0)); // Allocate new memory with StdMemAlloc else we crash. - memset(allocatedConvar, 0, 0xA0); // Set all to null. - std::uintptr_t cvarPtr = reinterpret_cast(allocatedConvar); // To ptr. - - *(void**)(cvarPtr + 0x40) = ICvarVtable.RCast(); // 0x40 to ICvar table. - *(void**)cvarPtr = ConVarVtable.RCast(); // 0x0 to ConVar vtable. - - CreateConVar.RCast() // Call to create ConVar. - (allocatedConvar, name, defaultValue, flags, helpString, bMin, fMin, bMax, fMax, callback, unk); - - return allocatedConvar; // Return allocated ConVar. - } - - void Script_RegisterFunction(void* sqvm, const char* name, const char* helpString, const char* retValType, const char* argTypes, void* funcPtr) - { - static MemoryAddress Script_RegisterFunction = MemoryAddress(0x141056040); - - SQFuncRegistration* func = new SQFuncRegistration(); - - func->scriptName = name; - func->nativeName = name; - func->helpString = helpString; - func->retValType = retValType; - func->argTypes = argTypes; - func->funcPtr = funcPtr; - - Script_RegisterFunction.RCast()(sqvm, func, 1); - } - - int Script_NativeTest(void* sqvm) - { - // function code goes here - - return 1; - } - - void RegisterUIScriptFunctions(void* sqvm) - { - //Script_RegisterFunction(sqvm, "UINativeTest", "native ui function", "void", "", &Script_NativeTest); - } - - void RegisterClientScriptFunctions(void* sqvm) - { - //Script_RegisterFunction(sqvm, "ClientNativeTest", "native client function", "void", "", &Script_NativeTest); - } - - void RegisterServerScriptFunctions(void* sqvm) - { - //Script_RegisterFunction(sqvm, "ServerNativeTest", "native server function", "void", "", &Script_NativeTest); - } - - void DisconnectClient(CClient* client, const char* reason, unsigned __int8 unk1, char unk2) - { - if (!client) // Client valid? - return; - - if (std::strlen(reason) == NULL) // Is reason null? - return; - - if (!client->GetNetChan()) - return; - - addr_NetChan_Shutdown(client->GetNetChan(), reason, unk1, unk2); // Shutdown netchan. - client->GetNetChan() = nullptr; // Null netchan. - MemoryAddress(0x140302FD0).RCast()(client); // Reset CClient instance for client. - } -} - -#pragma region KeyValues -const char* KeyValues::GetName() -{ - return GameGlobals::KeyValuesSystem->GetStringForSymbol(MAKE_3_BYTES_FROM_1_AND_2(m_iKeyNameCaseSensitive, m_iKeyNameCaseSensitive2)); -} -#pragma endregion diff --git a/r5dev/src/gui_utility.cpp b/r5dev/src/gui_utility.cpp deleted file mode 100644 index e991c749..00000000 --- a/r5dev/src/gui_utility.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "pch.h" -#include "gui_utility.h" - -GuiConfig g_GuiConfig; - -/*----------------------------------------------------------------------------- - * _gui_utility.cpp - *-----------------------------------------------------------------------------*/ - -int Stricmp(const char* s1, const char* s2) -{ - int d; - while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) - { - s1++; s2++; - } - return d; -} - -int Strnicmp(const char* s1, const char* s2, int n) -{ - int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) - { - s1++; s2++; n--; - } - return d; -} - -char* Strdup(const char* s) -{ - IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = malloc(len); IM_ASSERT(buf); if (buf != NULL) - { - return (char*)memcpy(buf, (const void*)s, len); - } - return NULL; -} - -void Strtrim(char* s) -{ - char* str_end = s + strlen(s); - - while (str_end > s && str_end[-1] == ' ') - str_end--; *str_end = 0; -} \ No newline at end of file diff --git a/r5dev/src/hooks/chlclient.cpp b/r5dev/src/hooks/chlclient.cpp deleted file mode 100644 index 63a83f4b..00000000 --- a/r5dev/src/hooks/chlclient.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - FrameStageNotifyFn originalFrameStageNotify = nullptr; -} - -void __fastcall Hooks::FrameStageNotify(CHLClient* rcx, ClientFrameStage_t curStage) -{ - switch (curStage) - { - case FRAME_START: // FrameStageNotify gets called every frame by CEngine::Frame with the stage being FRAME_START. We can use this to check/set global variables. - { - if (!GameGlobals::IsInitialized) - GameGlobals::InitGameGlobals(); - - break; - } - case FRAME_NET_UPDATE_POSTDATAUPDATE_END: - { - if (GameGlobals::BanSystem->IsRefuseListValid()) - { - for (int i = 0; i < GameGlobals::BanSystem->refuseList.size(); i++) // Loop through vector. - { - for (int c = 0; c < MAX_PLAYERS; c++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(c); // Get client instance. - if (!client) - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - int clientID = client->m_iUserID + 1; // Get UserID + 1. - if (clientID != GameGlobals::BanSystem->refuseList[i].second) // See if they match. - continue; - - GameGlobals::DisconnectClient(client, GameGlobals::BanSystem->refuseList[i].first.c_str(), 0, 1); - GameGlobals::BanSystem->DeleteConnectionRefuse(clientID); - break; - } - } - } - PatchNetVarConVar(); - break; - } - default: - break; - } - originalFrameStageNotify(rcx, curStage); -} \ No newline at end of file diff --git a/r5dev/src/hooks/connectclient.cpp b/r5dev/src/hooks/connectclient.cpp deleted file mode 100644 index 6b2a7360..00000000 --- a/r5dev/src/hooks/connectclient.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - ConnectClientFn originalConnectClient = nullptr;; -} - -void IsClientBanned(R5Net::Client* r5net, const std::string ip, std::int64_t orid) -{ - std::string err = std::string(); - bool compBanned = r5net && r5net->GetClientIsBanned(ip, orid, err); - if (compBanned) - { - while (compBanned) - { - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. - { - CClient* client = GameGlobals::Client->GetClientInstance(i); // Get client instance. - if (!client) // Client instance valid? - continue; - - if (!client->GetNetChan()) // Netchan valid? - continue; - - std::int64_t originID = client->m_iOriginID; // Get originID. - if (originID != orid) // See if they match. - continue; - - GameGlobals::BanSystem->AddConnectionRefuse(err, client->m_iUserID + 1); // Add to the vector. - compBanned = false; - break; - } - } - } -} - -void* Hooks::ConnectClient(void* thisptr, void* packet) -{ - if (!GameGlobals::BanSystem) - return originalConnectClient(thisptr, packet); - - std::string finalIPAddress = "null"; - MemoryAddress ipAddressField = MemoryAddress(((std::uintptr_t)packet + 0x10)); - if (ipAddressField && ipAddressField.GetValue() != 0x0) // The api they use to store the IP is pretty eh and idk any other way to do it rn. - { - std::stringstream ss; - ss << std::to_string(ipAddressField.GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." - << std::to_string(ipAddressField.Offset(0x3).GetValue()); - - finalIPAddress = ss.str(); - } - const char* name = *(const char**)((std::uintptr_t)packet + 0x30); // Get player name. - std::int64_t originID = *(std::int64_t*)((std::uintptr_t)packet + 0x28); // Get origin ID. - - if (std::strlen(name) == 0 || originID == 0) // Yeah that should not happen lol. - { - addr_CServer_RejectConnection(thisptr, *(unsigned int*)((std::uintptr_t)thisptr + 0xC), packet, "Invalid connection credentials."); // RejectConnection for the client. - } - - g_GameConsole->AddLog("[CServer::ConnectClient] %s is trying to connect. OriginID: %lld", name, originID); - - if (GameGlobals::BanSystem->IsBanListValid()) // Is the banlist vector valid? - { - if (GameGlobals::BanSystem->IsBanned(finalIPAddress, originID)) // Is the client trying to connect banned? - { - addr_CServer_RejectConnection(thisptr, *(unsigned int*)((std::uintptr_t)thisptr + 0xC), packet, "You have been banned from this Server."); // RejectConnection for the client. - g_GameConsole->AddLog("[CServer::ConnectClient] %s is banned. OriginID: %lld", name, originID); - return nullptr; - } - } - - if (g_CheckCompBanDB) - { - if (g_ServerBrowser) - { - R5Net::Client* r5net = g_ServerBrowser->GetR5Net(); - if (r5net) - { - std::thread t1(IsClientBanned, r5net, finalIPAddress, originID); - t1.detach(); - } - } - } - - return originalConnectClient(thisptr, packet); -} \ No newline at end of file diff --git a/r5dev/src/hooks/cvengineserver.cpp b/r5dev/src/hooks/cvengineserver.cpp deleted file mode 100644 index 73b84f1f..00000000 --- a/r5dev/src/hooks/cvengineserver.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - IsPersistenceDataAvailableFn originalIsPersistenceDataAvailable = nullptr; -} - -// TODO: turn this into a playerstruct constructor if it ever becomes necessary -bool Hooks::IsPersistenceDataAvailable(__int64 thisptr, int client) -{ - static bool isPersistenceVarSet[256]; - - // TODO: Maybe not hardcode - std::uintptr_t playerStructBase = 0x16073B200; - std::uintptr_t playerStructSize = 0x4A4C0; - std::uintptr_t persistenceVar = 0x5BC; - - std::uintptr_t targetPlayerStruct = playerStructBase + client * playerStructSize; - - *(char*)(targetPlayerStruct + persistenceVar) = (char)0x5; - - if (!isPersistenceVarSet[client]) - isPersistenceVarSet[client] = true; - - return originalIsPersistenceDataAvailable(thisptr, client); -} \ No newline at end of file diff --git a/r5dev/src/hooks/hooks.cpp b/r5dev/src/hooks/hooks.cpp deleted file mode 100644 index 515e928f..00000000 --- a/r5dev/src/hooks/hooks.cpp +++ /dev/null @@ -1,223 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -bool g_bBlockInput = false; - -namespace Hooks -{ - bool bToggledDevFlags = true; - bool bToggledNetTrace = false; -} - -void Hooks::InstallHooks() -{ - /////////////////////////////////////////////////////////////////////////////// - // Initialize Minhook - spdlog::debug("Hooking game functions now..\n"); - MH_Initialize(); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Squirrel functions - MH_CreateHook(addr_SQVM_Print, &Hooks::SQVM_Print, NULL); - MH_CreateHook(addr_SQVM_Warning, &Hooks::SQVM_Warning, reinterpret_cast(&originalSQVM_Warning)); - MH_CreateHook(addr_SQVM_LoadRson, &Hooks::SQVM_LoadRson, reinterpret_cast(&originalSQVM_LoadRson)); - MH_CreateHook(addr_SQVM_LoadScript, &Hooks::SQVM_LoadScript, reinterpret_cast(&originalSQVM_LoadScript)); - MH_CreateHook(addr_SQVM_RegisterOriginFuncs, &Hooks::SQVM_RegisterOriginFuncs, reinterpret_cast(&originalSQVM_RegisterOriginFuncs)); - MH_CreateHook(addr_SQVM_RegisterCreatePlayerTasklist, &Hooks::SQVM_RegisterCreatePlayerTasklist, reinterpret_cast(&originalSQVM_RegisterCreatePlayerTasklist)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook various Game functions - MH_CreateHook(addr_CHLClient_FrameStageNotify, &Hooks::FrameStageNotify, reinterpret_cast(&originalFrameStageNotify)); - MH_CreateHook(addr_CVEngineServer_IsPersistenceDataAvailable, &Hooks::IsPersistenceDataAvailable, reinterpret_cast(&originalIsPersistenceDataAvailable)); - MH_CreateHook(addr_CServer_ConnectClient, &Hooks::ConnectClient, reinterpret_cast(&originalConnectClient)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Netchan functions - MH_CreateHook(addr_NET_PrintFunc, &Hooks::NET_PrintFunc, reinterpret_cast(&originalNET_PrintFunc)); - MH_CreateHook(addr_NET_ReceiveDatagram, &Hooks::NET_ReceiveDatagram, reinterpret_cast(&originalNET_ReceiveDatagram)); - MH_CreateHook(addr_NET_SendDatagram, &Hooks::NET_SendDatagram, reinterpret_cast(&originalNET_SendDatagram)); - MH_CreateHook(addr_NetChan_Shutdown, &Hooks::NetChan_Shutdown, reinterpret_cast(&originalNetChan_ShutDown)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook ConVar | ConCommand functions - MH_CreateHook(addr_ConVar_IsFlagSet, &Hooks::ConVar_IsFlagSet, reinterpret_cast(&originalConVar_IsFlagSet)); - MH_CreateHook(addr_ConCommand_IsFlagSet, &Hooks::ConCommand_IsFlagSet, reinterpret_cast(&originalConCommand_IsFlagSet)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook CMatSystemSurface functions - MH_CreateHook(addr_CMatSystemSurface_LockCursor, &LockCursor, reinterpret_cast(&originalLockCursor)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook Utility functions - MH_CreateHook(addr_MSG_EngineError, &Hooks::MSG_EngineError, reinterpret_cast(&originalMSG_EngineError)); - MH_CreateHook(addr_LoadPlaylist, &Hooks::LoadPlaylist, reinterpret_cast(&originalLoadPlaylist)); - MH_CreateHook(addr_CEngineVGui_Paint, &Hooks::CEngineVGui_Paint, reinterpret_cast(&originalCEngineVGui_Paint)); - MH_CreateHook(addr_OriginGetErrorDescription, &Hooks::OriginGetErrorDescription, reinterpret_cast(&originalOriginGetErrorDescriptionWrapper)); - - /////////////////////////////////////////////////////////////////////////////// - // Hook WinAPI - if (Module user32dll = Module("user32.dll"); user32dll.GetModuleBase()) // Is user32.dll valid? - { - void* SetCursorPosPtr = user32dll.GetExportedFunction("SetCursorPos"); - void* ClipCursorPtr = user32dll.GetExportedFunction("ClipCursor"); - void* GetCursorPosPtr = user32dll.GetExportedFunction("GetCursorPos"); - void* ShowCursorPtr = user32dll.GetExportedFunction("ShowCursor"); - MH_CreateHook(SetCursorPosPtr, &Hooks::SetCursorPos, reinterpret_cast(&originalSetCursorPos)); - MH_CreateHook(ClipCursorPtr, &Hooks::ClipCursor, reinterpret_cast(&originalClipCursor)); - MH_CreateHook(GetCursorPosPtr, &Hooks::GetCursorPos, reinterpret_cast(&originalGetCursorPos)); - MH_CreateHook(ShowCursorPtr, &Hooks::ShowCursor, reinterpret_cast(&originalShowCursor)); - - /////////////////////////////////////////////////////////////////////////// - // Enable WinAPI hooks - MH_EnableHook(SetCursorPosPtr); - MH_EnableHook(ClipCursorPtr); - MH_EnableHook(GetCursorPosPtr); - MH_EnableHook(ShowCursorPtr); - } - - /////////////////////////////////////////////////////////////////////////////// - // Enable Squirrel hooks - MH_EnableHook(addr_SQVM_Print); - MH_EnableHook(addr_SQVM_Warning); - MH_EnableHook(addr_SQVM_LoadRson); - MH_EnableHook(addr_SQVM_LoadScript); - MH_EnableHook(addr_SQVM_RegisterOriginFuncs); - MH_EnableHook(addr_SQVM_RegisterCreatePlayerTasklist); - - /////////////////////////////////////////////////////////////////////////////// - // Enable various Game hooks - MH_EnableHook(addr_CHLClient_FrameStageNotify); - MH_EnableHook(addr_CVEngineServer_IsPersistenceDataAvailable); - MH_EnableHook(addr_CServer_ConnectClient); - - /////////////////////////////////////////////////////////////////////////////// - // Enable Netchan hooks - MH_EnableHook(addr_NET_PrintFunc); - MH_EnableHook(addr_NetChan_Shutdown); - - /////////////////////////////////////////////////////////////////////////////// - // Enable ConVar | ConCommand hooks - MH_EnableHook(addr_ConVar_IsFlagSet); - MH_EnableHook(addr_ConCommand_IsFlagSet); - - /////////////////////////////////////////////////////////////////////////////// - // Enable CMatSystemSurface hooks - MH_EnableHook(addr_CMatSystemSurface_LockCursor); - - /////////////////////////////////////////////////////////////////////////////// - // Enabled Utility hooks - MH_EnableHook(addr_MSG_EngineError); - MH_EnableHook(addr_LoadPlaylist); - MH_EnableHook(addr_CEngineVGui_Paint); - MH_EnableHook(addr_OriginGetErrorDescription); -} - -void Hooks::RemoveHooks() -{ - spdlog::debug("Unhooking game functions..\n"); - /////////////////////////////////////////////////////////////////////////////// - // Unhook Squirrel functions - MH_RemoveHook(addr_SQVM_Print); - MH_RemoveHook(addr_SQVM_LoadRson); - MH_RemoveHook(addr_SQVM_LoadScript); - MH_RemoveHook(addr_SQVM_RegisterOriginFuncs); - MH_RemoveHook(addr_SQVM_RegisterCreatePlayerTasklist); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook various Game Functions - MH_RemoveHook(addr_CHLClient_FrameStageNotify); - MH_RemoveHook(addr_CVEngineServer_IsPersistenceDataAvailable); - MH_RemoveHook(addr_CServer_ConnectClient); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Netchan functions - MH_RemoveHook(addr_NET_PrintFunc); - MH_RemoveHook(addr_NET_ReceiveDatagram); - MH_RemoveHook(addr_NET_SendDatagram); - MH_RemoveHook(addr_NetChan_Shutdown); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook ConVar | ConCommand functions - MH_RemoveHook(addr_ConVar_IsFlagSet); - MH_RemoveHook(addr_ConCommand_IsFlagSet); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook CMatSystemSurface functions - MH_RemoveHook(addr_CMatSystemSurface_LockCursor); - - /////////////////////////////////////////////////////////////////////////////// - // Unhook WinAPI - if (Module user32dll = Module("user32.dll"); user32dll.GetModuleBase()) // Is user32.dll valid? - { - void* SetCursorPosPtr = user32dll.GetExportedFunction("SetCursorPos"); - void* ClipCursorPtr = user32dll.GetExportedFunction("ClipCursor"); - void* GetCursorPosPtr = user32dll.GetExportedFunction("GetCursorPos"); - void* ShowCursorPtr = user32dll.GetExportedFunction("ShowCursor"); - - MH_RemoveHook(SetCursorPosPtr); - MH_RemoveHook(ClipCursorPtr); - MH_RemoveHook(GetCursorPosPtr); - MH_RemoveHook(ShowCursorPtr); - } - - /////////////////////////////////////////////////////////////////////////////// - // Unhook Utility functions - MH_RemoveHook(addr_MSG_EngineError); - MH_RemoveHook(addr_LoadPlaylist); - MH_RemoveHook(addr_CEngineVGui_Paint); - MH_RemoveHook(addr_OriginGetErrorDescription); - - /////////////////////////////////////////////////////////////////////////////// - // Reset Minhook - MH_Uninitialize(); -} - -void Hooks::ToggleNetTrace() -{ - if (!bToggledNetTrace) - { - MH_EnableHook(addr_NET_ReceiveDatagram); - MH_EnableHook(addr_NET_SendDatagram); - spdlog::info("\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("|>>>>>>>>>>>>>| NETCHANNEL TRACE ACTIVATED |<<<<<<<<<<<<<|\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("\n"); - } - else - { - MH_DisableHook(addr_NET_ReceiveDatagram); - MH_DisableHook(addr_NET_SendDatagram); - spdlog::info("\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("|>>>>>>>>>>>>| NETCHANNEL TRACE DEACTIVATED |<<<<<<<<<<<<|\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("\n"); - } - bToggledNetTrace = !bToggledNetTrace; -} - -void Hooks::ToggleDevCommands() -{ - if (!bToggledDevFlags) - { - MH_EnableHook(addr_ConVar_IsFlagSet); - MH_EnableHook(addr_ConCommand_IsFlagSet); - spdlog::info("\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("|>>>>>>>>>>>>>| DEVONLY COMMANDS ACTIVATED |<<<<<<<<<<<<<|\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("\n"); - } - else - { - MH_DisableHook(addr_ConVar_IsFlagSet); - MH_DisableHook(addr_ConCommand_IsFlagSet); - spdlog::info("\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("|>>>>>>>>>>>>| DEVONLY COMMANDS DEACTIVATED |<<<<<<<<<<<<|\n"); - spdlog::info("+--------------------------------------------------------+\n"); - spdlog::info("\n"); - } - bToggledDevFlags = !bToggledDevFlags; -} diff --git a/r5dev/src/hooks/hoststate.cpp b/r5dev/src/hooks/hoststate.cpp deleted file mode 100644 index 513debe6..00000000 --- a/r5dev/src/hooks/hoststate.cpp +++ /dev/null @@ -1,198 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - FrameUpdateFn originalFrameUpdate = nullptr; -} - -void Hooks::FrameUpdate(void* rcx, void* rdx, float time) -{ - static auto setjmpFn = MemoryAddress(0x141205460).RCast<__int64(*)(jmp_buf, void*)>(); - static auto host_abortserver = MemoryAddress(0x14B37C700).RCast(); - static auto CHostState_InitFn = MemoryAddress(0x14023E7D0).RCast(); - static auto g_ServerAbortServer = MemoryAddress(0x14B37CA22).RCast(); - static auto State_RunFn = MemoryAddress(0x14023E870).RCast(); - static auto Cbuf_ExecuteFn = MemoryAddress(0x14020D5C0).RCast(); - static auto g_ServerGameClients = MemoryAddress(0x14B383428).RCast<__int64*>(); - static auto SV_InitGameDLLFn = MemoryAddress(0x140308B90).RCast(); - static auto g_CModelLoader = MemoryAddress(0x14173B210).RCast(); - static auto CModelLoader_Map_IsValidFn = MemoryAddress(0x1402562F0).RCast(); - static auto Host_NewGameFn = MemoryAddress(0x140238DA0).RCast(); - static auto Host_Game_ShutdownFn = MemoryAddress(0x14023EDA0).RCast(); - static auto src_drawloading = MemoryAddress(0x14B37D96B).RCast(); - static auto scr_engineevent_loadingstarted = MemoryAddress(0x1666ED024).RCast(); - static auto gfExtendedError = MemoryAddress(0x14B383391).RCast(); - static auto g_CEngineVGui = MemoryAddress(0x141741310).RCast(); - static auto g_ServerDLL = MemoryAddress(0x141732048).RCast(); - static auto Host_ChangelevelFn = MemoryAddress(0x1402387B0).RCast(); - static auto CL_EndMovieFn = MemoryAddress(0x1402C03D0).RCast(); - static auto SendOfflineRequestToStryderFn = MemoryAddress(0x14033D380).RCast(); - static auto CEngine = MemoryAddress(0X141741BA0).RCast(); - - HostStates_t oldState; - void* placeHolder = nullptr; - if (setjmpFn(*host_abortserver, placeHolder)) - { - CHostState_InitFn(GameGlobals::HostState); - return; - } - else - { - *g_ServerAbortServer = true; - - do - { - Cbuf_ExecuteFn(); - oldState = GameGlobals::HostState->m_iCurrentState; - switch (GameGlobals::HostState->m_iCurrentState) - { - case HostStates_t::HS_NEW_GAME: - { - spdlog::debug("[+CHostState::FrameUpdate+] Starting new game now with level: {}\n", GameGlobals::HostState->m_levelName); - // Inlined CHostState::State_NewGame - GameGlobals::HostState->m_bSplitScreenConnect = false; - if (!g_ServerGameClients) // Init Game if it ain't valid. - { - SV_InitGameDLLFn(); - } - - if ( !CModelLoader_Map_IsValidFn(g_CModelLoader, GameGlobals::HostState->m_levelName) // Check if map is valid and if we can start a new game. - || !Host_NewGameFn(GameGlobals::HostState->m_levelName, nullptr, GameGlobals::HostState->m_bBackgroundLevel, GameGlobals::HostState->m_bSplitScreenConnect, nullptr) || !g_ServerGameClients) - { - spdlog::info("[+CHostState::FrameUpdate+] Fatal map error 1.\n"); - // Inlined SCR_EndLoadingPlaque - if (*src_drawloading) - { - *scr_engineevent_loadingstarted = 0; - using HideLoadingPlaqueFn = void(*)(void*); - (*reinterpret_cast(g_CEngineVGui))[36](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGui + 36))(&g_CEngineVGui);// HideLoadingPlaque - } - else if (*gfExtendedError) - { - using ShowErrorMessageFn = void(*)(void*); - (*reinterpret_cast(g_CEngineVGui))[35](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGui + 35))(&g_CEngineVGui);// ShowErrorMessage - } - // End Inline SCR_EndLoadingPlaque - - // Inlined CHostState::GameShutdown - if (GameGlobals::HostState->m_bActiveGame) - { - using GameShutdownFn = void(*)(void*); - (*reinterpret_cast(g_ServerDLL))[9](g_ServerDLL); // (*(void(__fastcall**)(void*))(*(_QWORD*)g_ServerDLL + 72i64))(g_ServerDLL);// GameShutdown - GameGlobals::HostState->m_bActiveGame = 0; - } - // End Inline CHostState::GameShutdown - } - - // Seems useless so nope. - // if (g_CHLClient) - // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); - - GameGlobals::HostState->m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. - - // If our next state isn't a shutdown or its a forced shutdown then set next state to run. - if (GameGlobals::HostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !GameGlobals::Cvar->FindVar("host_hasIrreversibleShutdown")->m_iValue) - GameGlobals::HostState->m_iNextState = HostStates_t::HS_RUN; - - // End Inline CHostState::State_NewGame - break; - } - case HostStates_t::HS_CHANGE_LEVEL_SP: - { - GameGlobals::HostState->m_flShortFrameTime = 1.5; // Set frame time. - - spdlog::debug("[+CHostState::FrameUpdate+] Changing singleplayer level to: {}.\n", GameGlobals::HostState->m_levelName); - - if (CModelLoader_Map_IsValidFn(g_CModelLoader, GameGlobals::HostState->m_levelName)) // Check if map is valid and if we can start a new game. - { - Host_ChangelevelFn(true, GameGlobals::HostState->m_levelName, GameGlobals::HostState->m_mapGroupName); // Call change level as singleplayer level. - } - else - { - spdlog::info("[+CHostState::FrameUpdate+] Server unable to change level, because unable to find map {}.\n", GameGlobals::HostState->m_levelName); - } - - // Seems useless so nope. - // if (g_CHLClient) - // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); - - GameGlobals::HostState->m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. - - // If our next state isn't a shutdown or its a forced shutdown then set next state to run. - if (GameGlobals::HostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !GameGlobals::Cvar->FindVar("host_hasIrreversibleShutdown")->m_iValue) - GameGlobals::HostState->m_iNextState = HostStates_t::HS_RUN; - - break; - } - case HostStates_t::HS_CHANGE_LEVEL_MP: - { - GameGlobals::HostState->m_flShortFrameTime = 0.5; // Set frame time. - using LevelShutdownFn = void(__thiscall*)(void*); - (*reinterpret_cast(*g_ServerDLL))[8](g_ServerDLL); // (*(void (__fastcall **)(void *))(*(_QWORD *)server_dll_var + 64i64))(server_dll_var);// LevelShutdown - - spdlog::debug("[+CHostState::FrameUpdate+] Changing multiplayer level to: {}.\n", GameGlobals::HostState->m_levelName); - - if (CModelLoader_Map_IsValidFn(g_CModelLoader, GameGlobals::HostState->m_levelName)) // Check if map is valid and if we can start a new game. - { - using EnabledProgressBarForNextLoadFn = void(*)(void*); - (*reinterpret_cast(g_CEngineVGui))[31](g_CEngineVGui); // (*((void(__fastcall**)(void**))g_CEngineVGUI + 31))(&g_CEngineVGUI);// EnabledProgressBarForNextLoad - Host_ChangelevelFn(false, GameGlobals::HostState->m_levelName, GameGlobals::HostState->m_mapGroupName); // Call change level as multiplayer level. - } - else - { - spdlog::info("[+CHostState::FrameUpdate+] Server unable to change level, because unable to find map {}.\n", GameGlobals::HostState->m_levelName); - } - - // Seems useless so nope. - // // if (g_CHLClient) - // (*(void(__fastcall**)(__int64, _QWORD))(*(_QWORD*)g_CHLClient + 1000i64))(g_CHLClient, 0i64); - - GameGlobals::HostState->m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. - - // If our next state isn't a shutdown or its a forced shutdown then set next state to run. - if (GameGlobals::HostState->m_iNextState != HostStates_t::HS_SHUTDOWN || !GameGlobals::Cvar->FindVar("host_hasIrreversibleShutdown")->m_iValue) - GameGlobals::HostState->m_iNextState = HostStates_t::HS_RUN; - - break; - } - case HostStates_t::HS_RUN: - { - State_RunFn(&GameGlobals::HostState->m_iCurrentState, nullptr, time); - break; - } - case HostStates_t::HS_GAME_SHUTDOWN: - { - spdlog::debug("[+CHostState::FrameUpdate+] Shutting game down now.\n"); - Host_Game_ShutdownFn(GameGlobals::HostState); - break; - } - case HostStates_t::HS_RESTART: - { - spdlog::debug("[+CHostState::FrameUpdate+] Restarting client.\n"); - CL_EndMovieFn(); - SendOfflineRequestToStryderFn(); // We have hostnames nulled anyway. - *(std::int32_t*)((std::uintptr_t)CEngine + 0xC) = 3; //g_CEngine.vtable->SetNextState(&g_CEngine, DLL_RESTART); - break; - } - case HostStates_t::HS_SHUTDOWN: - { - spdlog::debug("[+CHostState::FrameUpdate+] Shutting client down.\n"); - CL_EndMovieFn(); - SendOfflineRequestToStryderFn(); // We have hostnames nulled anyway. - *(std::int32_t*)((std::uintptr_t)CEngine + 0xC) = 2; //g_CEngine.vtable->SetNextState(&g_CEngine, DLL_CLOSE); - break; - } - default: - { - break; - } - } - - } while ((oldState != HostStates_t::HS_RUN || GameGlobals::HostState->m_iNextState == HostStates_t::HS_LOAD_GAME && GameGlobals::Cvar->FindVar("g_single_frame_shutdown_for_reload_cvar")->m_pParent->m_iValue) - && oldState != HostStates_t::HS_SHUTDOWN - && oldState != HostStates_t::HS_RESTART); - - } -// originalFrameUpdate(rcx, rdx, time); -} \ No newline at end of file diff --git a/r5dev/src/hooks/iconvar.cpp b/r5dev/src/hooks/iconvar.cpp deleted file mode 100644 index ef384a0f..00000000 --- a/r5dev/src/hooks/iconvar.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -#ifdef _DEBUG -#define MaskOffCheats -#endif - -namespace Hooks -{ - ConVar_IsFlagSetFn originalConVar_IsFlagSet = nullptr; - ConCommand_IsFlagSetFn originalConCommand_IsFlagSet = nullptr; -} - -bool Hooks::ConVar_IsFlagSet(ConVar* cvar, int flag) -{ -#ifdef MaskOffCheats - if (g_bDebugConsole) - { - std::cout << "--------------------------------------------------\n"; - std::cout << cvar->m_ConCommandBase.m_pszName << " Flags: " << std::hex << std::uppercase << cvar->m_ConCommandBase.m_nFlags << "\n"; - } - // Mask off FCVAR_DEVELOPMENTONLY and FCVAR_CHEAT. - cvar->m_ConCommandBase.m_nFlags &= ~(FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT); - if (g_bDebugConsole) - { - std::cout << cvar->m_ConCommandBase.m_pszName << " Flags: " << std::hex << std::uppercase << cvar->m_ConCommandBase.m_nFlags << "\n"; - std::cout << cvar->m_ConCommandBase.m_pszName << " Verify: " << std::hex << std::uppercase << flag << "\n"; - std::cout << "--------------------------------------------------\n"; - } - - if (flag & FCVAR_RELEASE) - { - return true; - } - - if (g_bReturnAllFalse) - { - return false; - } - - return (cvar->m_ConCommandBase.m_nFlags & flag) != 0; -#else - // Mask off FCVAR_DEVELOPMENTONLY if existing. - cvar->m_ConCommandBase.m_nFlags &= ~FCVAR_DEVELOPMENTONLY; - - return originalConVar_IsFlagSet(cvar, flag); -#endif -} - -bool Hooks::ConCommand_IsFlagSet(ConCommandBase* cmd, int flag) -{ -#ifdef MaskOffCheats - if (g_bDebugConsole) - { - std::cout << "--------------------------------------------------\n"; - std::cout << cmd->m_pszName << " Flags: " << std::hex << std::uppercase << cmd->m_nFlags << "\n"; - } - // Mask off FCVAR_DEVELOPMENTONLY and FCVAR_CHEAT. - cmd->m_nFlags &= ~(FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT); - if (g_bDebugConsole) - { - std::cout << cmd->m_pszName << " Flags: " << std::hex << std::uppercase << cmd->m_nFlags << "\n"; - std::cout << cmd->m_pszName << " Verify: " << std::hex << std::uppercase << flag << "\n"; - std::cout << "--------------------------------------------------\n"; - } - - if (flag & FCVAR_RELEASE) - { - return true; - } - - if (g_bReturnAllFalse) - { - return false; - } - - return (cmd->m_nFlags & flag) != 0; -#else - // Mask off FCVAR_DEVELOPMENTONLY if existing. - cmd->m_nFlags &= ~FCVAR_DEVELOPMENTONLY; - - return originalConCommand_IsFlagSet(cmd, flag); -#endif -} \ No newline at end of file diff --git a/r5dev/src/hooks/loadplaylist.cpp b/r5dev/src/hooks/loadplaylist.cpp deleted file mode 100644 index c362daec..00000000 --- a/r5dev/src/hooks/loadplaylist.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - LoadPlaylistFn originalLoadPlaylist = nullptr; -} - -bool Hooks::LoadPlaylist(const char* playlist) -{ - memset(addr_MapVPKCache, 0, 0x40); // Bye bye vpk cache, you only make us crash >:(. - - CHAR playlistPath[] = "\x77\x27\x35\x2b\x2c\x6c\x2b\x2c\x2b"; - PCHAR curr = playlistPath; - while (*curr) { - *curr ^= 'B'; - ++curr; - } - - if (FileExists(playlistPath)) - { - std::uint8_t verifyPlaylistIntegrity[] = // Very hacky way for alternative inline assembly for x64.. - { - 0x48, 0x8B, 0x45, 0x58, // mov rcx, playlist - 0xC7, 0x00, 0x00, 0x00, 0x00, // test playlist, playlist - 0x00 - }; - void* verifyPlaylistIntergrityFn = nullptr; - VirtualAlloc(verifyPlaylistIntergrityFn, 10, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - memcpy(&verifyPlaylistIntergrityFn, (const void*)verifyPlaylistIntegrity, 9); - reinterpret_cast(verifyPlaylistIntergrityFn)(); - } - - return originalLoadPlaylist(playlist); // Parse playlist like normally.. -} \ No newline at end of file diff --git a/r5dev/src/hooks/lockcursor.cpp b/r5dev/src/hooks/lockcursor.cpp deleted file mode 100644 index 676fb70d..00000000 --- a/r5dev/src/hooks/lockcursor.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "id3dx.h" - -namespace Hooks -{ - LockCursorFn originalLockCursor = nullptr; -} - -void Hooks::LockCursor(void* thisptr) -{ - if (g_bShowConsole || g_bShowBrowser) - { - addr_CMatSystemSurface_UnlockCursor(thisptr); // Unlock cursor if our gui is shown. - return; - } - return originalLockCursor(thisptr); -} \ No newline at end of file diff --git a/r5dev/src/hooks/msgbox.cpp b/r5dev/src/hooks/msgbox.cpp deleted file mode 100644 index 4846a4f6..00000000 --- a/r5dev/src/hooks/msgbox.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - MSG_EngineErrorFn originalMSG_EngineError = nullptr; -} - -int Hooks::MSG_EngineError(char* fmt, va_list args) -{ - std::cout << "\nENGINE ERROR #####################################\n"; - vprintf(fmt, args); - - return originalMSG_EngineError(fmt, args); -} \ No newline at end of file diff --git a/r5dev/src/hooks/net.cpp b/r5dev/src/hooks/net.cpp deleted file mode 100644 index 096d33d4..00000000 --- a/r5dev/src/hooks/net.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "logsystem.h" - -namespace Hooks -{ - NET_PrintFuncFn originalNET_PrintFunc = nullptr; - NET_ReceiveDatagramFn originalNET_ReceiveDatagram = nullptr; - NET_SendDatagramFn originalNET_SendDatagram = nullptr; -} - -static std::ostringstream oss_print; -static auto ostream_sink_print = std::make_shared(oss_print); -static auto log_sink_print = std::make_shared("logs/NET_Print.txt", true); - -//----------------------------------------------------------------------------- -// Purpose: log the clients signonstate to the console -//----------------------------------------------------------------------------- -void Hooks::NET_PrintFunc(const char* fmt, ...) -{ - static bool initialized = false; - static char buf[1024]; - - oss_print.str(""); - oss_print.clear(); - - static spdlog::logger logger("sqvm_print", { log_sink_print, ostream_sink_print }); - - if (!initialized) - { - log_sink_print->set_level(spdlog::level::debug); - ostream_sink_print->set_level(spdlog::level::debug); - logger.set_level(spdlog::level::debug); - logger.set_pattern("[%S.%e] %v"); - - initialized = true; - } - - va_list args; - va_start(args, fmt); - - vsnprintf(buf, sizeof(buf), fmt, args); - - buf[sizeof(buf) - 1] = 0; - va_end(args); - - logger.debug(buf); - - std::string s = oss_print.str(); - const char* c = s.c_str(); - - g_LogSystem.AddLog(LogType_t::NATIVE, s); - - Items.push_back(Strdup((const char*)c)); -} - -//----------------------------------------------------------------------------- -// Purpose: hook and log the receive datagram -//----------------------------------------------------------------------------- -bool Hooks::NET_ReceiveDatagram(int sock, void* inpacket, bool raw) -{ - bool result = originalNET_ReceiveDatagram(sock, inpacket, raw); - if (result) - { - int i = NULL; - netpacket_t* pkt = (netpacket_t*)inpacket; - - /////////////////////////////////////////////////////////////////////////// - // Log received packet data - HexDump("[+] NET_ReceiveDatagram", 0, &pkt->data[i], pkt->wiresize); - } - - return result; -} - -//----------------------------------------------------------------------------- -// Purpose: hook and log the send datagram -//----------------------------------------------------------------------------- -unsigned int Hooks::NET_SendDatagram(SOCKET s, const char* buf, int len, int flags) -{ - unsigned int result = originalNET_SendDatagram(s, buf, len, flags); - if (result) - { - /////////////////////////////////////////////////////////////////////////// - // Log transmitted packet data - HexDump("[+] NET_SendDatagram", 0, buf, len); - } - - return result; -} \ No newline at end of file diff --git a/r5dev/src/hooks/netchannel.cpp b/r5dev/src/hooks/netchannel.cpp deleted file mode 100644 index 6ad9ffa3..00000000 --- a/r5dev/src/hooks/netchannel.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - NetChan_ShutDown originalNetChan_ShutDown = nullptr; -} - -void Hooks::NetChan_Shutdown(void* rcx, const char* reason, unsigned __int8 unk1, char unk2) -{ - addr_downloadPlaylists_Callback(); // Re-Load playlist from disk after getting dropped or disconnecting off a server. - originalNetChan_ShutDown(rcx, reason, unk1, unk2); -} \ No newline at end of file diff --git a/r5dev/src/hooks/originsdk.cpp b/r5dev/src/hooks/originsdk.cpp deleted file mode 100644 index 6233208c..00000000 --- a/r5dev/src/hooks/originsdk.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - OriginGetErrorDescriptionWrapperFn originalOriginGetErrorDescriptionWrapper = nullptr; -} - -const char* Hooks::OriginGetErrorDescription(std::uint32_t originCode) -{ - switch (originCode) - { - case ORIGIN_ERROR_CORE_AUTHENTICATION_FAILED: - { - MessageBoxA(NULL, "Origin LSX Authentication challenge failed.\nAre you perhaps not logged into Origin?", "R5 Reloaded", MB_OK); - break; - } - case ORIGIN_ERROR_SDK_NOT_INITIALIZED: - { - MessageBoxA(NULL, "Origin SDK was not initialized.", "R5 Reloaded", MB_OK); - break; - } - case ORIGIN_ERROR_CORE_NOTLOADED: - { - MessageBoxA(NULL, "Origin Desktop Application is not loaded.", "R5 Reloaded", MB_OK); - break; - } - case ORIGIN_ERROR_CORE_LOGIN_FAILED: - { - MessageBoxA(NULL, "Origin couldn't authenticate with the Origin Servers.", "R5 Reloaded", MB_OK); - break; - } - case ORIGIN_ERROR_CORE_NOT_INSTALLED: - { - MessageBoxA(NULL, "Origin is not installed on this machine or could not be found.\nOrigin is needed to run R5 Reloaded.", "R5 Reloaded", MB_OK); - break; - } - default: - break; - } - - return originalOriginGetErrorDescriptionWrapper(originCode); -} \ No newline at end of file diff --git a/r5dev/src/hooks/sqvm.cpp b/r5dev/src/hooks/sqvm.cpp deleted file mode 100644 index 34a02f36..00000000 --- a/r5dev/src/hooks/sqvm.cpp +++ /dev/null @@ -1,221 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "logsystem.h" - -namespace Hooks -{ - SQVM_WarningFn originalSQVM_Warning = nullptr; - SQVM_LoadRsonFn originalSQVM_LoadRson = nullptr; - SQVM_LoadScriptFn originalSQVM_LoadScript = nullptr; - SQVM_RegisterOriginFuncsFn originalSQVM_RegisterOriginFuncs = nullptr; - SQVM_RegisterCreatePlayerTasklistFn originalSQVM_RegisterCreatePlayerTasklist = nullptr; -} - -static std::ostringstream oss_print; -static auto ostream_sink_print = std::make_shared(oss_print); -static auto log_sink_print = std::make_shared("logs/SQVM_Print.txt", true); - -//--------------------------------------------------------------------------------- -// Purpose: prints the output of each VM to the console -//--------------------------------------------------------------------------------- -void* Hooks::SQVM_Print(void* sqvm, char* fmt, ...) -{ - static bool initialized = false; - static char buf[1024]; - static std::string vmType[3] = { "Script(S):", "Script(C):", "Script(U):" }; - - int vmIdx = *(int*)((std::uintptr_t)sqvm + 0x18); - std::string vmStr = vmType[vmIdx].c_str(); - - oss_print.str(""); - oss_print.clear(); - - static spdlog::logger logger("sqvm_print", { log_sink_print, ostream_sink_print }); - - if (!initialized) - { - log_sink_print->set_level(spdlog::level::debug); - ostream_sink_print->set_level(spdlog::level::debug); - logger.set_level(spdlog::level::debug); - logger.set_pattern("[%S.%e] %v"); - - initialized = true; - } - - va_list args; - va_start(args, fmt); - - vsnprintf(buf, sizeof(buf), fmt, args); - - buf[sizeof(buf) - 1] = 0; - va_end(args); - - vmStr.append(buf); - - if (g_GameConsole && g_GameConsole->ShouldPrintToCommandPrompt()) - { - spdlog::info(vmStr); - } - - logger.debug(vmStr); - - - std::string s = oss_print.str(); - const char* c = s.c_str(); - - g_LogSystem.AddLog((LogType_t)vmIdx, s); - - Items.push_back(Strdup(c)); - - return NULL; -} - -static std::ostringstream oss_warning; -static auto ostream_sink_warning = std::make_shared(oss_warning); -static auto log_sink_warning = std::make_shared("logs/SQVM_Warning.txt", true); - -__int64 Hooks::SQVM_Warning(void* sqvm, int a2, int a3, int* stringSize, void** string) -{ - __int64 result = originalSQVM_Warning(sqvm, a2, a3, stringSize, string); - - void* retaddr = _ReturnAddress(); // Get return address. - - if (retaddr != addr_SQVM_Warning_ReturnAddr) // Check if its SQVM_Warning calling. - return result; // If not return. - - static bool initialized = false; - static std::string vmType[3] = { "Script(S) Warning:", "Script(C) Warning:", "Script(U) Warning:" }; - - int vmIdx = *(int*)((std::uintptr_t)sqvm + 0x18); - std::string vmStr = vmType[vmIdx].c_str(); - - oss_print.str(""); - oss_print.clear(); - - static spdlog::logger logger("sqvm_warning", { log_sink_warning, ostream_sink_warning }); - - if (!initialized) - { - log_sink_warning->set_level(spdlog::level::debug); - ostream_sink_warning->set_level(spdlog::level::debug); - logger.set_level(spdlog::level::debug); - logger.set_pattern("[%S.%e] %v"); - - initialized = true; - } - - std::string stringConstructor((char*)*string, *stringSize); // Get string from memory via std::string constructor. - vmStr.append(stringConstructor); - - if (g_GameConsole && g_GameConsole->ShouldPrintToCommandPrompt()) - { - spdlog::info(vmStr); - } - - logger.debug(vmStr); - - std::string s = oss_warning.str(); - const char* c = s.c_str(); - - g_LogSystem.AddLog((LogType_t)vmIdx, s); - - Items.push_back(Strdup(c)); - - return result; -} - -//--------------------------------------------------------------------------------- -// Purpose: loads the include file from the mods directory -//--------------------------------------------------------------------------------- -__int64 Hooks::SQVM_LoadRson(const char* rson_name) -{ - char filepath[MAX_PATH] = { 0 }; - sprintf_s(filepath, MAX_PATH, "platform\\%s", rson_name); - - /////////////////////////////////////////////////////////////////////////////// - // Flip forward slashes in filepath to windows-style backslash - for (int i = 0; i < strlen(filepath); i++) - { - if (filepath[i] == '/') - { - filepath[i] = '\\'; - } - } - - /////////////////////////////////////////////////////////////////////////////// - // Returns the new path if the rson exists on the disk - if (FileExists(filepath) && originalSQVM_LoadRson(rson_name)) - { - spdlog::info("\n"); - spdlog::info("##################################################\n"); - spdlog::info("] '{}'\n", filepath); - spdlog::info("##################################################\n"); - spdlog::info("\n"); - - return originalSQVM_LoadRson(filepath); - } - - spdlog::info("\n"); - spdlog::info("##################################################\n"); - spdlog::info("] '{}'\n", rson_name); - spdlog::info("##################################################\n"); - spdlog::info("\n"); - - return originalSQVM_LoadRson(rson_name); -} - -//--------------------------------------------------------------------------------- -// Purpose: loads the script file from the mods directory -//--------------------------------------------------------------------------------- -bool Hooks::SQVM_LoadScript(void* sqvm, const char* script_path, const char* script_name, int flag) -{ - char filepath[MAX_PATH] = { 0 }; - sprintf_s(filepath, MAX_PATH, "platform\\%s", script_path); - - /////////////////////////////////////////////////////////////////////////////// - // Flip forward slashes in filepath to windows-style backslash - for (int i = 0; i < strlen(filepath); i++) - { - if (filepath[i] == '/') - { - filepath[i] = '\\'; - } - } - if (g_bDebugLoading) - { - spdlog::info(" [+] Loading SQVM Script '{}' ...\n", filepath); - } - /////////////////////////////////////////////////////////////////////////////// - // Returns true if the script exists on the disk - if (FileExists(filepath) && originalSQVM_LoadScript(sqvm, filepath, script_name, flag)) - { - return true; - } - if (g_bDebugLoading) - { - spdlog::info(" [!] FAILED. Try SP / VPK for '%s'\n", filepath); - } - - return originalSQVM_LoadScript(sqvm, script_path, script_name, flag); -} - -void Hooks::SQVM_RegisterOriginFuncs(void* sqvm) -{ - static MemoryAddress UIVM = MemoryAddress(0x14D4151F0); - - originalSQVM_RegisterOriginFuncs(sqvm); - - if (sqvm == *(UIVM.RCast())) { - GameGlobals::RegisterUIScriptFunctions(sqvm); - } - else { - GameGlobals::RegisterClientScriptFunctions(sqvm); - } -} - -void Hooks::SQVM_RegisterCreatePlayerTasklist(void* sqvm) -{ - GameGlobals::RegisterServerScriptFunctions(sqvm); - - originalSQVM_RegisterCreatePlayerTasklist(sqvm); -} \ No newline at end of file diff --git a/r5dev/src/hooks/winapi.cpp b/r5dev/src/hooks/winapi.cpp deleted file mode 100644 index a9a96905..00000000 --- a/r5dev/src/hooks/winapi.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "pch.h" -#include "hooks.h" - -namespace Hooks -{ - namespace - { - static POINT g_pLastCursorPos{ 0 }; - } - - GetCursorPosFn originalGetCursorPos = nullptr; - SetCursorPosFn originalSetCursorPos = nullptr; - ClipCursorFn originalClipCursor = nullptr; - ShowCursorFn originalShowCursor = nullptr; -} - -BOOL WINAPI Hooks::GetCursorPos(LPPOINT lpPoint) -{ - if (g_bBlockInput) - { - assert(lpPoint != nullptr); - *lpPoint = g_pLastCursorPos; - } - - return originalGetCursorPos(lpPoint); -} - -BOOL WINAPI Hooks::SetCursorPos(int X, int Y) -{ - g_pLastCursorPos.x = X; - g_pLastCursorPos.y = Y; - - if (g_bBlockInput) - { - return TRUE; - } - - return originalSetCursorPos(X, Y); -} - -BOOL WINAPI Hooks::ClipCursor(const RECT* lpRect) -{ - if (g_bBlockInput) - { - lpRect = nullptr; - } - - return originalClipCursor(lpRect); -} - -BOOL WINAPI Hooks::ShowCursor(BOOL bShow) -{ - if (g_bBlockInput) - { - bShow = TRUE; - } - - return originalShowCursor(bShow); -} \ No newline at end of file diff --git a/r5dev/src/id3dx.cpp b/r5dev/src/id3dx.cpp deleted file mode 100644 index 6adce1a2..00000000 --- a/r5dev/src/id3dx.cpp +++ /dev/null @@ -1,415 +0,0 @@ -#include "pch.h" - -#include "id3dx.h" -#include "hooks.h" -#include "console.h" -#include "patterns.h" -#include "gameclasses.h" - -#include "CCompanion.h" -#include "CGameConsole.h" - -#pragma comment(lib, "d3d11.lib") -/////////////////////////////////////////////////////////////////////////////////// -// Type definitions. -typedef HRESULT(__stdcall* IDXGISwapChainPresent)(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags); -typedef HRESULT(__stdcall* IDXGIResizeBuffers) (IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags); - -/////////////////////////////////////////////////////////////////////////////////// -// Variables. -bool g_bShowConsole = false; -bool g_bShowBrowser = false; -IDXGISwapChainPresent g_fnIDXGISwapChainPresent = nullptr; -IDXGIResizeBuffers g_fnIDXGIResizeBuffers = nullptr; -ID3D11Device* g_pDevice = nullptr; -ID3D11RenderTargetView* g_pMainRenderTargetView = nullptr; -ID3D11DeviceContext* g_pDeviceContext = nullptr; -WNDPROC originalWndProc = NULL; -DWORD g_dThreadId = NULL; - -LRESULT CALLBACK DXGIMsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - return DefWindowProc(hWnd, uMsg, wParam, lParam); -} - -LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN) - { - if (wParam == g_GuiConfig.CGameConsoleConfig.bind1 || wParam == g_GuiConfig.CGameConsoleConfig.bind2) - { - g_bShowConsole = !g_bShowConsole; - } - - if (wParam == g_GuiConfig.CCompanionConfig.bind1 || wParam == g_GuiConfig.CCompanionConfig.bind2) - { - g_bShowBrowser = !g_bShowBrowser; - } - } - - if (g_bShowConsole || g_bShowBrowser) - { - g_bBlockInput = true; // Prevent mouse cursor from being modified if console is open. - if (ImGui_ImplWin32_WndProcHandler(hwnd, uMsg, wParam, lParam) > 0) - return 1L; - } - else - { - g_bBlockInput = false; // Allow mouse input. - } - - return CallWindowProc(originalWndProc, hwnd, uMsg, wParam, lParam); -} - -void InitRenderer() -{ - spdlog::debug("Registering temporary Window for DirectX..\n"); - // Register temporary window instance to get DirectX 11 relevant virtual function ptr. - WNDCLASSEX ws; - ws.cbSize = sizeof(WNDCLASSEX); - ws.style = CS_HREDRAW | CS_VREDRAW; - ws.lpfnWndProc = DXGIMsgProc; - ws.cbClsExtra = 0; - ws.cbWndExtra = 0; - ws.hInstance = GetModuleHandle(NULL); - ws.hIcon = NULL; - ws.hCursor = NULL; - ws.hbrBackground = NULL; - ws.lpszMenuName = NULL; - ws.lpszClassName = "R5 Reloaded"; - ws.hIconSm = NULL; - - RegisterClassEx(&ws); - - // Create temporary window. - HWND window = CreateWindowA(ws.lpszClassName, "R5 Reloaded Window", WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, NULL, NULL, ws.hInstance, NULL); - - DXGI_RATIONAL refreshRate; - refreshRate.Numerator = 60; - refreshRate.Denominator = 1; - - D3D_FEATURE_LEVEL featureLevel; - const D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 }; - - // Setup buffer description. - DXGI_MODE_DESC bufferDescription; - bufferDescription.Width = 100; - bufferDescription.Height = 100; - bufferDescription.RefreshRate = refreshRate; - bufferDescription.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - bufferDescription.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; - bufferDescription.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; - - DXGI_SAMPLE_DESC sampleDescription; - sampleDescription.Count = 1; - sampleDescription.Quality = 0; - - // Setup swap chain description. - DXGI_SWAP_CHAIN_DESC swapChainDescription; - swapChainDescription.BufferDesc = bufferDescription; - swapChainDescription.SampleDesc = sampleDescription; - swapChainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDescription.BufferCount = 1; - swapChainDescription.OutputWindow = window; - swapChainDescription.Windowed = TRUE; - swapChainDescription.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - swapChainDescription.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; - - IDXGISwapChain* swapChain; - ID3D11Device* device; - ID3D11DeviceContext* context; - - // Create temporary fake device and swap chain. - if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, featureLevels, 1, D3D11_SDK_VERSION, &swapChainDescription, &swapChain, &device, &featureLevel, &context))) - { - std::cout << "Creating Device and Swap Chain failed." << std::endl; - return; - } - - DWORD_PTR* swapChainVTable = nullptr; - DWORD_PTR* contextVTable = nullptr; - DWORD_PTR* deviceVTable = nullptr; - - // Get vtable by dereferencing once. - swapChainVTable = (DWORD_PTR*)swapChain; - swapChainVTable = (DWORD_PTR*)swapChainVTable[0]; - contextVTable = (DWORD_PTR*)context; - contextVTable = (DWORD_PTR*)contextVTable[0]; - deviceVTable = (DWORD_PTR*)device; - deviceVTable = (DWORD_PTR*)deviceVTable[0]; - - // Get virtual functions addresses. - g_fnIDXGISwapChainPresent = (IDXGISwapChainPresent)(DWORD_PTR)swapChainVTable[(int)DXGISwapChainVTbl::Present]; - g_fnIDXGIResizeBuffers = (IDXGIResizeBuffers)(DWORD_PTR)swapChainVTable[(int)DXGISwapChainVTbl::ResizeBuffers]; - - // Safe release all relevant ptrs. - swapChain->Release(); - swapChain = nullptr; - - device->Release(); - device = nullptr; - - context->Release(); - context = nullptr; - - // Destroy Window used for getting the virtual functions addresses and unregister its class. - DestroyWindow(swapChainDescription.OutputWindow); - UnregisterClass(ws.lpszClassName, ws.hInstance); -} - -bool LoadTextureFromByteArray(unsigned char* image_data, const int& image_width, const int& image_height, ID3D11ShaderResourceView** out_srv) -{ - // Load from disk into a raw RGBA buffer - //int image_width = 0; - //int image_height = 0; - //unsigned char* image_data = stbi_load(filename, &image_width, &image_height, NULL, 4); - if (image_data == NULL) - { - return false; - } - - // Create texture - D3D11_TEXTURE2D_DESC desc; - ID3D11Texture2D* pTexture = NULL; - D3D11_SUBRESOURCE_DATA subResource; - D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; - - 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; - - subResource.pSysMem = image_data; - subResource.SysMemPitch = desc.Width * 4; - subResource.SysMemSlicePitch = 0; - - g_pDevice->CreateTexture2D(&desc, &subResource, &pTexture); - - // Create texture view - 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; - - if (pTexture) - { - g_pDevice->CreateShaderResourceView(pTexture, &srvDesc, out_srv); - } - pTexture->Release(); - - //stbi_image_free(image_data); - - return true; -} - -void DrawMenu() -{ - if (!GameGlobals::IsInitialized || !GameGlobals::InputSystem) // Check if GameGlobals initialized and if InputSystem is valid. - return; - - // Handle new ImGui frame. - ImGui_ImplDX11_NewFrame(); - ImGui_ImplWin32_NewFrame(); - ImGui::NewFrame(); - - // Init class instances. - static CGameConsole console; - static bool AssignConsolePtr = []() { - g_GameConsole = &console; - spdlog::debug("[+CGameConsole+] Created CGameConsole Class instance.\n"); - return true; - } (); - - static CCompanion companion; - static bool AssignCompanionPtr = []() { - g_ServerBrowser = &companion; - spdlog::debug("[+CCompanion+] Created CCompanion Class instance.\n"); - return true; - } (); - - // Handle game input if one of the menus is open. - if (g_bShowConsole || g_bShowBrowser) - { - GameGlobals::InputSystem->EnableInput(false); - } - else - { - GameGlobals::InputSystem->EnableInput(true); - } - - if (g_bShowConsole) - { - console.Draw("Console"); - } - - if (g_bShowBrowser) - { - companion.Draw("Companion"); - } - - // Handle end of frame and prepare rendering. - ImGui::EndFrame(); - ImGui::Render(); - - // Set new render target. - // This breaks 4:3 in main menu and load screen if not applying the games DepthStencilView. Applying the games DepthStencilView makes ImGui not render tho. - g_pDeviceContext->OMSetRenderTargets(1, &g_pMainRenderTargetView, NULL); - ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); // Tell ImGui to render all the draw data. -} - -IDXGIResizeBuffers originalResizeBuffers = nullptr; -HRESULT __stdcall GetResizeBuffers(IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags) -{ - spdlog::debug("Resizing IDXGIResizeBuffers..\n"); - // Re-create render target if our window got resized. - if (g_pMainRenderTargetView) - { - g_pDeviceContext->OMSetRenderTargets(0, 0, 0); // Set render target to null. - - // Safe release the render target. - g_pMainRenderTargetView->Release(); - g_pMainRenderTargetView = nullptr; - } - - ImGui_ImplDX11_InvalidateDeviceObjects(); // Invalidate all ImGui DirectX objects. - - HRESULT hr = originalResizeBuffers(pSwapChain, nBufferCount, nWidth, nHeight, dxFormat, nSwapChainFlags); // Let DirectX resize all the buffers. - - if (!g_pDevice) // Valid device? - return hr; - - ID3D11Texture2D* pBuffer; - pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBuffer); // Grab the swapchains buffer. - - if (!pBuffer) // Valid buffer? - return hr; - - g_pDevice->CreateRenderTargetView(pBuffer, NULL, &g_pMainRenderTargetView); // Create render target again with the new swapchain buffer. - - // Safe release the buffer. - pBuffer->Release(); - pBuffer = nullptr; - - if (!g_pMainRenderTargetView) // Valid render target? - return hr; - - g_pDeviceContext->OMSetRenderTargets(1, &g_pMainRenderTargetView, NULL); // Set new render target. - - // Set up the viewport. - D3D11_VIEWPORT vp; - vp.Width = static_cast(nWidth); - vp.Height = static_cast(nHeight); - vp.MinDepth = 0.0f; - vp.MaxDepth = 1.0f; - vp.TopLeftX = 0; - vp.TopLeftY = 0; - g_pDeviceContext->RSSetViewports(1, &vp); - - ImGui_ImplDX11_CreateDeviceObjects(); // Create new DirectX objects for ImGui. - - return hr; -} - -IDXGISwapChainPresent originalPresent = nullptr; -HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags) -{ - static bool InitializedPresent = false; - if (!InitializedPresent) - { - spdlog::debug("Initializing IDXGISwapChainPresent hook..\n"); - if (SUCCEEDED(pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&g_pDevice))) // Get device via swap chain. - { - g_pDevice->GetImmediateContext(&g_pDeviceContext); // Get device context via device. - DXGI_SWAP_CHAIN_DESC sd; - pSwapChain->GetDesc(&sd); // Get the swap chain description. - ID3D11Texture2D* pBuffer; - pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBuffer); // Get swap chain buffer. - - if (!pBuffer) // Valid buffer? - return originalPresent(pSwapChain, nSyncInterval, nFlags); - - g_pDevice->CreateRenderTargetView(pBuffer, NULL, &g_pMainRenderTargetView); // Create new render target. - - // Safe release the buffer. - pBuffer->Release(); - pBuffer = nullptr; - - originalWndProc = (WNDPROC)SetWindowLongPtr(sd.OutputWindow, GWLP_WNDPROC, (LONG_PTR)WindowProc); // Hook current output window. - - // Initialize ImGui. - ImGui::CreateContext(); - ImGui_ImplWin32_Init(sd.OutputWindow); - ImGui_ImplDX11_Init(g_pDevice, g_pDeviceContext); - - InitializedPresent = true; - } - else - { - return originalPresent(pSwapChain, nSyncInterval, nFlags); - } - } - - DrawMenu(); - return originalPresent(pSwapChain, nSyncInterval, nFlags); -} - -void InstallDXHooks() -{ - spdlog::debug("Initializing DirectX hooks..\n"); - MH_CreateHook(g_fnIDXGISwapChainPresent, &Present, reinterpret_cast(&originalPresent)); - MH_CreateHook(g_fnIDXGIResizeBuffers, &GetResizeBuffers, reinterpret_cast(&originalResizeBuffers)); - - MH_EnableHook(g_fnIDXGISwapChainPresent); - MH_EnableHook(g_fnIDXGIResizeBuffers); -} - -void RemoveDXHooks() -{ - spdlog::debug("Removing DirectX hooks..\n"); - MH_RemoveHook(g_fnIDXGISwapChainPresent); - MH_RemoveHook(g_fnIDXGIResizeBuffers); - - ImGui_ImplWin32_Shutdown(); - ImGui_ImplDX11_Shutdown(); -} - -void PrintDXAddress() -{ - std::cout << "+--------------------------------------------------------+" << std::endl; - std::cout << "| ID3D11DeviceContext : " << std::hex << std::uppercase << g_pDeviceContext << std::setw(13) << " |" << std::endl; - std::cout << "| ID3D11Device : " << std::hex << std::uppercase << g_pDevice << std::setw(13) << " |" << std::endl; - std::cout << "| ID3D11RenderTargetView : " << std::hex << std::uppercase << g_pMainRenderTargetView << std::setw(13) << " |" << std::endl; - std::cout << "| IDXGISwapChainPresent : " << std::hex << std::uppercase << g_fnIDXGISwapChainPresent << std::setw(13) << " |" << std::endl; - std::cout << "+--------------------------------------------------------+" << std::endl; -} - -//################################################################################# -// ENTRYPOINT -//################################################################################# - -DWORD __stdcall DXSwapChainWorker(LPVOID) -{ - InitRenderer(); - InstallDXHooks(); - return true; -} - -void SetupDXSwapChain() -{ - spdlog::debug("Setting up DirectX thread..\n"); - // Create a worker thread for the console overlay - DWORD __stdcall DXSwapChainWorker(LPVOID); - HANDLE hThread = CreateThread(NULL, 0, DXSwapChainWorker, NULL, 0, &g_dThreadId); - - if (hThread) - { - CloseHandle(hThread); - } -} \ No newline at end of file diff --git a/r5dev/src/input.cpp b/r5dev/src/input.cpp deleted file mode 100644 index e85ccc19..00000000 --- a/r5dev/src/input.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "pch.h" -#include "input.h" \ No newline at end of file diff --git a/r5dev/src/logsystem.cpp b/r5dev/src/logsystem.cpp deleted file mode 100644 index 78b7e8dc..00000000 --- a/r5dev/src/logsystem.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "pch.h" -#include "hooks.h" -#include "logsystem.h" - -LogSystem g_LogSystem; - -void LogSystem::Update() -{ - if (GameGlobals::Cvar->FindVar("cl_drawconsoleoverlay")->m_iValue < 1) - return; - - if (m_vLogs.empty()) - return; - - static void* pCMatSystemSurface = MemoryAddress(0x14D40B360).RCast(); - if (!pCMatSystemSurface) - return; - - static int fontHeight = 16; - - for (int i = 0; i < m_vLogs.size(); ++i) - { - if (m_vLogs[i].Ticks >= 0) - { - if (i < GameGlobals::Cvar->FindVar("cl_consoleoverlay_lines")->m_iValue) - { - float fadepct = fminf(static_cast(m_vLogs[i].Ticks) / 64.f, 1.0); - float ptc = static_cast(ceilf( fadepct * 255.f)); - int alpha = static_cast(ptc); - int y = (10 + (fontHeight * i)); - - std::array color = GetLogColorForType(m_vLogs[i].Type); - addr_CMatSystemSurface_DrawColoredText(pCMatSystemSurface, 0x13, fontHeight, 10, y, color[0], color[1], color[2], alpha, m_vLogs[i].Message.c_str()); - } - else - { - m_vLogs.erase(m_vLogs.begin()); - continue; - } - - m_vLogs[i].Ticks--; - } - else - { - m_vLogs.erase(m_vLogs.begin() + i); - } - } -} - -void LogSystem::AddLog(LogType_t type, std::string message) -{ - if (message.length() > 0) - { - m_vLogs.push_back(Log{ message, 1024, type }); - } -} - -std::array LogSystem::GetLogColorForType(LogType_t type) -{ - switch (type) - { - case LogType_t::NATIVE: - return { 255, 255, 255 }; - case LogType_t::SCRIPT_SERVER: - return { 190, 183, 240 }; - case LogType_t::SCRIPT_CLIENT: - return { 117, 116, 139 }; - case LogType_t::SCRIPT_UI: - return { 197, 160, 177 }; - default: - return { 255, 255, 255 }; - } - - return { 255, 255, 255 }; -} diff --git a/r5dev/src/opcptc.cpp b/r5dev/src/opcptc.cpp deleted file mode 100644 index 3fd0b8fa..00000000 --- a/r5dev/src/opcptc.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "pch.h" -#include "opcptc.h" - -/*----------------------------------------------------------------------------- - * _opcptc.cpp - *-----------------------------------------------------------------------------*/ - -void InstallOpcodes() /* .TEXT */ -{ - spdlog::debug("Patching the game executeable..\n"); - //------------------------------------------------------------------------- - // JNZ --> JMP | Prevent OriginSDK from initializing - //Origin_Init.Offset(0x0B).Patch({ 0xE9, 0x63, 0x02, 0x00, 0x00, 0x00 }); - //Origin_SetState.Offset(0x0E).Patch({ 0xE9, 0xCB, 0x03, 0x00, 0x00 }); - //------------------------------------------------------------------------- - // JNE --> JMP | Allow games to be loaded without the optional texture streaming file - dst002.Offset(0x8E5).Patch({ 0xEB, 0x19 }); - //------------------------------------------------------------------------- - // JNE --> JMP | Prevent connect command from crashing by invalid call to UI function - dst004.Offset(0x1D6).Patch({ 0xEB, 0x27 }); - //------------------------------------------------------------------------- - // JNE --> JMP | Prevent connect localhost from being executed after listenserver init - //Host_NewGame.Offset(0x637).Patch({ 0xE9, 0xC1, 0x00, 0x00, 0x00}); - //------------------------------------------------------------------------- - // JA --> JMP | Disable server-side verification for duplicate accounts on the server - CServer_Auth.Offset(0x284).Patch({ 0x90, 0x90 }); - //------------------------------------------------------------------------- - // JA --> JMP | Prevent FairFight anti-cheat from initializing on the server - FairFight_Init.Offset(0x61).Patch({ 0xE9, 0xED, 0x00, 0x00, 0x00, 0x00 }); - //------------------------------------------------------------------------- - // CALL --> NOP | Prevent squirrel compiler errors from calling Error - Squirrel_CompileError.Offset(0x12C).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); - //------------------------------------------------------------------------- - // CALL --> NOP | Prevent random netchan encryption key from being overriden by default key - NetChan_EncKey_DefaultAssign.Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); - //------------------------------------------------------------------------- - // INLINE CALL --> VTABLE CALL | Call LockCursor VTable class function instead of doing it inlined. - //------------------------------------------------------------------------- - // .text:0000000140548E2C 80 3D 5C 2E 1D 01 00 cmp cs:byte_14171BC8F, 0 - // .text:0000000140548E33 48 8B 0D 16 25 EC 0C mov rcx, cs:g_InputStackSystem - // .text:0000000140548E3A 48 8B 97 18 01 00 00 mov rdx, [rdi+118h] - // .text:0000000140548E41 C6 05 91 7B EC 0C 01 mov cs:byte_14D4109D9, 1 - // .text:0000000140548E48 48 8B 01 mov rax, [rcx] - // .text:0000000140548E4B 74 10 jz short loc_140548E5D - // .text:0000000140548E4D 4C 8B 05 8C 7B EC 0C mov r8, cs:qword_14D4109E0 - // .text:0000000140548E54 48 83 C4 30 add rsp, 30h - // .text:0000000140548E58 5F pop rdi - // .text:0000000140548E59 48 FF 60 60 jmp qword ptr[rax+60h] - //------------------------------------------------------------------------- - // TURNS INTO: - //------------------------------------------------------------------------- - // .text:0000000140548E2C 48 8B 07 mov rax, [rdi] - // .text:0000000140548E2F 48 89 F9 mov rcx, rdi - // .text:0000000140548E32 FF 90 90 02 00 00 call qword ptr[rax+290h] - // .text:0000000140548E38 EB 2F jmp short loc_140548E69 - //------------------------------------------------------------------------- - MemoryAddress(0x140548E2C).Patch({ 0x48, 0x8B, 0x07, 0x48, 0x89, 0xF9, 0xFF, 0x90, 0x90, 0x02, 0x00, 0x00, 0xEB, 0x2F }); -} diff --git a/r5dev/src/pch.cpp b/r5dev/src/pch.cpp deleted file mode 100644 index 17305716..00000000 --- a/r5dev/src/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" \ No newline at end of file diff --git a/r5dev/src/squirrel.cpp b/r5dev/src/squirrel.cpp deleted file mode 100644 index 584af969..00000000 --- a/r5dev/src/squirrel.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "pch.h" -#include "patterns.h" - -char* sq_getstring(void* sqvm, int i) -{ - std::uintptr_t thisptr = reinterpret_cast(sqvm); - - return *(char**)(*(__int64*)(thisptr + 0x58) + 0x10 * i + 0x8) + 0x40; -} - -int sq_getinteger(void* sqvm, int i) -{ - std::uintptr_t thisptr = reinterpret_cast(sqvm); - - return *(int*)(*(__int64*)(thisptr + 0x58) + 0x10 * i + 0x8); -} - -void sq_pushbool(void* sqvm, int val) -{ - addr_sq_pushbool(sqvm, val); -} - -void sq_pushstring(void* sqvm, char* string, int len) -{ - addr_sq_pushstring(sqvm, string, len); -} - -void sq_pushstring(void* sqvm, const char* string, int len) -{ - addr_sq_pushstring(sqvm, const_cast(string), len); -} - -void sq_pushinteger(void* sqvm, int val) -{ - addr_sq_pushinteger(sqvm, val); -} - -void sq_newarray(void* sqvm, int size) -{ - addr_sq_newarray(sqvm, size); -} - -void sq_arrayappend(void* sqvm, int idx) -{ - addr_sq_arrayappend(sqvm, idx); -} - -void sq_newtable(void* sqvm) -{ - addr_sq_newtable(sqvm); -} - -void sq_newslot(void* sqvm, int idx) -{ - addr_sq_newslot(sqvm, idx); -} \ No newline at end of file diff --git a/external/detours/include/detours.h b/r5dev/thirdparty/detours/include/detours.h similarity index 100% rename from external/detours/include/detours.h rename to r5dev/thirdparty/detours/include/detours.h diff --git a/external/detours/include/detver.h b/r5dev/thirdparty/detours/include/detver.h similarity index 100% rename from external/detours/include/detver.h rename to r5dev/thirdparty/detours/include/detver.h diff --git a/r5dev/thirdparty/detours/include/idetour.h b/r5dev/thirdparty/detours/include/idetour.h new file mode 100644 index 00000000..63474cfe --- /dev/null +++ b/r5dev/thirdparty/detours/include/idetour.h @@ -0,0 +1,39 @@ +#include + +#ifndef IDETOUR_H +#define IDETOUR_H + +#define ADDDETOUR(x,y) static size_t dummy_reg_##y = AddDetour( new x() ); +#define XREGISTER(x,y) ADDDETOUR(x, y) +#define REGISTER(x) XREGISTER(x, __COUNTER__) + +class IDetour +{ +public: + virtual ~IDetour() { ; } + //virtual void attach() = 0; + //virtual void detach() = 0; + virtual void debugp() = 0; +}; + +namespace +{ + std::int32_t npad = 9; + std::vector vdetour; + size_t AddDetour(IDetour* idtr) + { + vdetour.push_back(idtr); + return vdetour.size(); + } +} + +class H : public IDetour +{ + virtual void debugp() + { + // + } +}; + +REGISTER(H); +#endif // IDETOUR_H diff --git a/external/detours/include/syelog.h b/r5dev/thirdparty/detours/include/syelog.h similarity index 100% rename from external/detours/include/syelog.h rename to r5dev/thirdparty/detours/include/syelog.h diff --git a/r5dev/thirdparty/detours/libs/detours.lib b/r5dev/thirdparty/detours/libs/detours.lib new file mode 100644 index 00000000..f85304f4 Binary files /dev/null and b/r5dev/thirdparty/detours/libs/detours.lib differ diff --git a/r5dev/thirdparty/detours/libs/syelog.lib b/r5dev/thirdparty/detours/libs/syelog.lib new file mode 100644 index 00000000..35e7e5b7 Binary files /dev/null and b/r5dev/thirdparty/detours/libs/syelog.lib differ diff --git a/external/imgui/include/imconfig.h b/r5dev/thirdparty/imgui/include/imconfig.h similarity index 88% rename from external/imgui/include/imconfig.h rename to r5dev/thirdparty/imgui/include/imconfig.h index 39de21c6..7082c550 100644 --- a/external/imgui/include/imconfig.h +++ b/r5dev/thirdparty/imgui/include/imconfig.h @@ -33,17 +33,20 @@ // It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp. //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended. -//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger window: ShowMetricsWindow() will be empty. +//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger and other debug tools: ShowMetricsWindow() and ShowStackToolWindow() will be empty. //---- Don't implement some functions to reduce linkage requirements. //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) -//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. (imm32.lib/.a) +//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) +//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. -//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. +//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) +//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). +//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available //---- Include imgui_user.h at the end of imgui.h as a convenience //#define IMGUI_INCLUDE_IMGUI_USER_H @@ -67,7 +70,7 @@ //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). -// On Windows you may use vcpkg with 'vcpkg install freetype' + 'vcpkg integrate install'. +// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. //#define IMGUI_ENABLE_FREETYPE //---- Use stb_truetype to build and rasterize the font atlas (default) diff --git a/external/imgui/include/imgui.h b/r5dev/thirdparty/imgui/include/imgui.h similarity index 93% rename from external/imgui/include/imgui.h rename to r5dev/thirdparty/imgui/include/imgui.h index 72c197e5..ff0a44be 100644 --- a/external/imgui/include/imgui.h +++ b/r5dev/thirdparty/imgui/include/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.83 WIP +// dear imgui, v1.86 // (headers) // Help: @@ -11,11 +11,14 @@ // - FAQ http://dearimgui.org/faq // - Homepage & latest https://github.com/ocornut/imgui // - Releases & changelog https://github.com/ocornut/imgui/releases -// - Gallery https://github.com/ocornut/imgui/issues/3793 (please post your screenshots/video there!) +// - Gallery https://github.com/ocornut/imgui/issues/4451 (please post your screenshots/video there!) // - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Glossary https://github.com/ocornut/imgui/wiki/Glossary // - Issues & support https://github.com/ocornut/imgui/issues -// - Discussions https://github.com/ocornut/imgui/discussions + +// Getting Started? +// - For first-time users having issues compiling/linking/running or issues loading fonts: +// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. /* @@ -60,8 +63,8 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.83 WIP" -#define IMGUI_VERSION_NUM 18204 +#define IMGUI_VERSION "1.86" +#define IMGUI_VERSION_NUM 18600 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE @@ -89,18 +92,31 @@ Index of this file: #endif // Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions. -#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__clang__) -#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) -#define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0))) -#elif !defined(IMGUI_USE_STB_SPRINTF) && defined(__GNUC__) && defined(__MINGW32__) +#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__) #define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) #define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0))) +#elif !defined(IMGUI_USE_STB_SPRINTF) && (defined(__clang__) || defined(__GNUC__)) +#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) +#define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0))) #else #define IM_FMTARGS(FMT) #define IM_FMTLIST(FMT) #endif +// Disable some of MSVC most aggressive Debug runtime checks in function header/footer (used in some simple/low-level functions) +#if defined(_MSC_VER) && !defined(__clang__) && !defined(IMGUI_DEBUG_PARANOID) +#define IM_MSVC_RUNTIME_CHECKS_OFF __pragma(runtime_checks("",off)) __pragma(check_stack(off)) __pragma(strict_gs_check(push,off)) +#define IM_MSVC_RUNTIME_CHECKS_RESTORE __pragma(runtime_checks("",restore)) __pragma(check_stack()) __pragma(strict_gs_check(pop)) +#else +#define IM_MSVC_RUNTIME_CHECKS_OFF +#define IM_MSVC_RUNTIME_CHECKS_RESTORE +#endif + // Warnings +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). +#endif #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wold-style-cast" @@ -136,7 +152,7 @@ struct ImGuiContext; // Dear ImGui context (opaque structure, unl struct ImGuiIO; // Main configuration and I/O between your application and ImGui struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) struct ImGuiListClipper; // Helper to manually clip large list of items -struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame, used by IMGUI_ONCE_UPON_A_FRAME macro +struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use) struct ImGuiStorage; // Helper for key->value storage @@ -147,7 +163,7 @@ struct ImGuiTextBuffer; // Helper to hold and append into a text buf struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbbb][,ccccc]") struct ImGuiViewport; // A Platform Window (always only one in 'master' branch), in the future may represent Platform Monitor -// Enums/Flags (declared as int for compatibility with old C++, to allow using as flags and to not pollute the top of this file) +// Enums/Flags (declared as int for compatibility with old C++, to allow using as flags without overhead, and to not pollute the top of this file) // - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists! // In Visual Studio IDE: CTRL+comma ("Edit.NavigateTo") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. // With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. @@ -187,27 +203,22 @@ typedef int ImGuiTreeNodeFlags; // -> enum ImGuiTreeNodeFlags_ // Flags: f typedef int ImGuiViewportFlags; // -> enum ImGuiViewportFlags_ // Flags: for ImGuiViewport typedef int ImGuiWindowFlags; // -> enum ImGuiWindowFlags_ // Flags: for Begin(), BeginChild() -// Other types -#ifndef ImTextureID // ImTextureID [configurable type: override in imconfig.h with '#define ImTextureID xxx'] -typedef void* ImTextureID; // User data for rendering backend to identify a texture. This is whatever to you want it to be! read the FAQ about ImTextureID for details. -#endif -typedef unsigned int ImGuiID; // A unique ID used by widgets, typically hashed from a stack of string. -typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText() -typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints() -typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() -typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() - -// Character types -// (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display) -typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings. -typedef unsigned int ImWchar32; // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings. -#ifdef IMGUI_USE_WCHAR32 // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16] -typedef ImWchar32 ImWchar; -#else -typedef ImWchar16 ImWchar; +// ImTexture: user data for renderer backend to identify a texture [Compile-time configurable type] +// - To use something else than an opaque void* pointer: override with e.g. '#define ImTextureID MyTextureType*' in your imconfig.h file. +// - This can be whatever to you want it to be! read the FAQ about ImTextureID for details. +#ifndef ImTextureID +typedef void* ImTextureID; // Default: store a pointer or an integer fitting in a pointer (most renderer backends are ok with that) #endif -// Basic scalar data types +// ImDrawIdx: vertex index. [Compile-time configurable type] +// - To use 16-bit indices + allow large meshes: backend need to set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset (recommended). +// - To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in your imconfig.h file. +#ifndef ImDrawIdx +typedef unsigned short ImDrawIdx; // Default: 16-bit (for maximum compatibility with renderer backends) +#endif + +// Scalar data types +typedef unsigned int ImGuiID;// A unique ID used by widgets (typically the result of hashing a stack of string) typedef signed char ImS8; // 8-bit signed integer typedef unsigned char ImU8; // 8-bit unsigned integer typedef signed short ImS16; // 16-bit signed integer @@ -226,7 +237,25 @@ typedef signed long long ImS64; // 64-bit signed integer (post C++11) typedef unsigned long long ImU64; // 64-bit unsigned integer (post C++11) #endif -// 2D vector (often used to store positions or sizes) +// Character types +// (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display) +typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings. +typedef unsigned int ImWchar32; // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings. +#ifdef IMGUI_USE_WCHAR32 // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16] +typedef ImWchar32 ImWchar; +#else +typedef ImWchar16 ImWchar; +#endif + +// Callback and functions types +typedef int (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data); // Callback function for ImGui::InputText() +typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); // Callback function for ImGui::SetNextWindowSizeConstraints() +typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() +typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() + +// ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type] +// This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type. +IM_MSVC_RUNTIME_CHECKS_OFF struct ImVec2 { float x, y; @@ -239,7 +268,7 @@ struct ImVec2 #endif }; -// 4D vector (often used to store floating-point colors) +// ImVec4: 4D vector used to store clipping rectangles, colors etc. [Compile-time configurable type] struct ImVec4 { float x, y, z, w; @@ -249,6 +278,7 @@ struct ImVec4 IM_VEC4_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4. #endif }; +IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] Dear ImGui end-user API functions @@ -277,6 +307,7 @@ namespace ImGui // Demo, Debug, Information IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc. + IMGUI_API void ShowStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID. IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information. IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. @@ -328,7 +359,8 @@ namespace ImGui IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x) IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y) - // Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). + // Window manipulation + // - Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0, 0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc. IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin() IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Sizes will be rounded down. Use callback to apply non-trivial programmatic constraints. @@ -340,7 +372,7 @@ namespace ImGui IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0, 0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). IMGUI_API void SetWindowFocus(); // (not recommended) set current window to be focused / top-most. prefer using SetNextWindowFocus(). - IMGUI_API void SetWindowFontScale(float scale); // set font scale. Adjust IO.FontGlobalScale if you want to scale all windows. This is an old API! For correct scaling, prefer to reload font + rebuild ImFontAtlas + call style.ScaleAllSizes(). + IMGUI_API void SetWindowFontScale(float scale); // [OBSOLETE] set font scale. Adjust IO.FontGlobalScale if you want to scale all windows. This is an old API! For correct scaling, prefer to reload font + rebuild ImFontAtlas + call style.ScaleAllSizes(). IMGUI_API void SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond = 0); // set named window position. IMGUI_API void SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond = 0); // set named window size. set axis to 0.0f to force an auto-fit on this axis. IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond = 0); // set named window collapsed state @@ -351,9 +383,8 @@ namespace ImGui // - Those functions are bound to be redesigned (they are confusing, incomplete and the Min/Max return values are in local window coordinates which increases confusion) IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates - IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates - IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates - IMGUI_API float GetWindowContentRegionWidth(); // + IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min for the full window (roughly (0,0)-Scroll), in window coordinates + IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max for the full window (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates // Windows Scrolling IMGUI_API float GetScrollX(); // get scrolling amount [0 .. GetScrollMaxX()] @@ -376,7 +407,7 @@ namespace ImGui IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); // modify a style float variable. always use this if you modify the style after NewFrame(). IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); // modify a style ImVec2 variable. always use this if you modify the style after NewFrame(). IMGUI_API void PopStyleVar(int count = 1); - IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets + IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // == tab stop enable. Allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets IMGUI_API void PopAllowKeyboardFocus(); IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. IMGUI_API void PopButtonRepeat(); @@ -390,6 +421,7 @@ namespace ImGui IMGUI_API void PopTextWrapPos(); // Style read access + // - Use the style editor (ShowStyleEditor() function) to interactively see what the colors are) IMGUI_API ImFont* GetFont(); // get current font IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API @@ -430,11 +462,15 @@ namespace ImGui IMGUI_API float GetFrameHeightWithSpacing(); // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets) // ID stack/scopes - // - Read the FAQ for more details about how ID are handled in dear imgui. If you are creating widgets in a loop you most - // likely want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them. - // - The resulting ID are hashes of the entire stack. + // Read the FAQ (docs/FAQ.md or http://dearimgui.org/faq) for more details about how ID are handled in dear imgui. + // - Those questions are answered and impacted by understanding of the ID stack system: + // - "Q: Why is my widget not reacting when I click on it?" + // - "Q: How can I have widgets with an empty label?" + // - "Q: How can I have multiple widgets with the same label?" + // - Short version: ID are hashes of the entire ID stack. If you are creating widgets in a loop you most likely + // want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them. // - You can also use the "Label##foobar" syntax within widget label to distinguish them from each others. - // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed and used as an ID, + // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed + used as an ID, // whereas "str_id" denote a string that is only used as an ID and not normally displayed. IMGUI_API void PushID(const char* str_id); // push string into the ID stack (will hash string). IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); // push string into the ID stack (will hash string). @@ -487,12 +523,12 @@ namespace ImGui IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_max_height_in_items = -1); // Widgets: Drag Sliders - // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped and can go off-bounds. + // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). - // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits. + // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used. // - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum. // - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them. // - Legacy: Pre-1.78 there are DragXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. @@ -511,7 +547,7 @@ namespace ImGui IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0); // Widgets: Regular Sliders - // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped and can go off-bounds. + // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). // - Legacy: Pre-1.78 there are SliderXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. @@ -548,7 +584,7 @@ namespace ImGui IMGUI_API bool InputDouble(const char* label, double* v, double step = 0.0, double step_fast = 0.0, const char* format = "%.6f", ImGuiInputTextFlags flags = 0); IMGUI_API bool InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); IMGUI_API bool InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0); - IMGUI_API bool Hotkey(const char* label, int* key, const ImVec2& size); + IMGUI_API bool Hotkey(const char* label, int* key, const ImVec2& size); // Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little color square that can be left-clicked to open a picker, and right-clicked to open an option menu.) // - Note that in C++ a 'float v[X]' function argument is the _same_ as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. @@ -598,7 +634,7 @@ namespace ImGui IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); // Widgets: Data Plotting - // - Consider using ImPlot (https://github.com/epezent/implot) + // - Consider using ImPlot (https://github.com/epezent/implot) which is much better! IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float)); IMGUI_API void PlotLines(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0)); IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float)); @@ -640,12 +676,14 @@ namespace ImGui // - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered(). // - IMPORTANT: Popup identifiers are relative to the current ID stack, so OpenPopup and BeginPopup generally needs to be at the same level of the stack. // This is sometimes leading to confusing mistakes. May rework this in the future. + // Popups: begin/end functions // - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards. ImGuiWindowFlags are forwarded to the window. // - BeginPopupModal(): block every interactions behind the window, cannot be closed by user, add a dimming background, has a title bar. IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it. IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // return true if the modal is open, and you can start outputting to it. IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! + // Popups: open/close functions // - OpenPopup(): set popup state to open. ImGuiPopupFlags are available for opening options. // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. @@ -657,6 +695,7 @@ namespace ImGui IMGUI_API void OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) IMGUI_API void CloseCurrentPopup(); // manually close the popup we have begin-ed into. + // Popups: open+begin combined functions helpers // - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking. // - They are convenient to easily create context menus, hence the name. @@ -665,6 +704,7 @@ namespace ImGui IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);// open+begin popup when clicked on current window. IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked in void (where there are no windows). + // Popups: query functions // - IsPopupOpen(): return true if the popup is open at the current BeginPopup() level of the popup stack. // - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId: return true if any popup is open at the current BeginPopup() level of the popup stack. @@ -701,6 +741,7 @@ namespace ImGui IMGUI_API void TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row. IMGUI_API bool TableNextColumn(); // append into the next column (or first column of next row if currently in last column). Return true when column is visible. IMGUI_API bool TableSetColumnIndex(int column_n); // append into the specified column. Return true when column is visible. + // Tables: Headers & Columns declaration // - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc. // - Use TableHeadersRow() to create a header row and automatically submit a TableHeader() for each column. @@ -713,6 +754,7 @@ namespace ImGui IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled. IMGUI_API void TableHeadersRow(); // submit all headers cells based on data provided to TableSetupColumn() + submit context menu IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used) + // Tables: Sorting // - Call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting. // - When 'SpecsDirty == true' you should sort your data. It will be true when sorting specs have changed @@ -720,6 +762,7 @@ namespace ImGui // wastefully sort your data every frame! // - Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable(). IMGUI_API ImGuiTableSortSpecs* TableGetSortSpecs(); // get latest sort specs for the table (NULL if not sorting). + // Tables: Miscellaneous functions // - Functions args 'int column_n' treat the default value of -1 as the same as passing the current column index. IMGUI_API int TableGetColumnCount(); // return number of columns (value passed to BeginTable) @@ -727,10 +770,10 @@ namespace ImGui IMGUI_API int TableGetRowIndex(); // return current row index. IMGUI_API const char* TableGetColumnName(int column_n = -1); // return "" if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column. IMGUI_API ImGuiTableColumnFlags TableGetColumnFlags(int column_n = -1); // return column flags so you can query their Enabled/Visible/Sorted/Hovered status flags. Pass -1 to use current column. - IMGUI_API void TableSetColumnEnabled(int column_n, bool v);// change enabled/disabled state of a column, set to false to hide the column. Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) + IMGUI_API void TableSetColumnEnabled(int column_n, bool v);// change user accessible enabled/disabled state of a column. Set to false to hide the column. User can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) IMGUI_API void TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n = -1); // change the color of a cell, row, or column. See ImGuiTableBgTarget_ flags for details. - // Legacy Columns API (2020: prefer using Tables!) + // Legacy Columns API (prefer using Tables!) // - You can also use SameLine(pos_x) to mimic simplified columns. IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true); IMGUI_API void NextColumn(); // next column, defaults to current row or next row if the current row is finished @@ -772,6 +815,13 @@ namespace ImGui IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. may return NULL. use ImGuiPayload::IsDataType() to test for the payload type. + // Disabling [BETA API] + // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) + // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) + // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. + IMGUI_API void BeginDisabled(bool disabled = true); + IMGUI_API void EndDisabled(); + // Clipping // - Mouse hovering is affected by ImGui::PushClipRect() calls, unlike direct calls to ImDrawList::PushClipRect() which are render only. IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); @@ -820,7 +870,6 @@ namespace ImGui IMGUI_API const char* GetStyleColorName(ImGuiCol idx); // get a string corresponding to the enum value (for display, saving, etc.). IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it) IMGUI_API ImGuiStorage* GetStateStorage(); - IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // calculate coarse clipping for large list of evenly sized items. Prefer using the ImGuiListClipper higher-level helper if you can. IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame IMGUI_API void EndChildFrame(); // always call EndChildFrame() regardless of BeginChildFrame() return values (which indicates a collapsed/clipped window) @@ -848,9 +897,10 @@ namespace ImGui // - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle. // - Dragging operations are only reported after mouse has moved a certain distance away from the initial clicking position (see 'lock_threshold' and 'io.MouseDraggingThreshold') IMGUI_API bool IsMouseDown(ImGuiMouseButton button); // is mouse button held? - IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down) + IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down). Same as GetMouseClickedCount() == 1. IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down) - IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? (note that a double-click will also report IsMouseClicked() == true) + IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? Same as GetMouseClickedCount() == 2. (note that a double-click will also report IsMouseClicked() == true) + IMGUI_API int GetMouseClickedCount(ImGuiMouseButton button); // return the number of successive mouse-clicks at the time where a click happen (otherwise 0). IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available IMGUI_API bool IsAnyMouseDown(); // is any mouse button held? @@ -871,6 +921,7 @@ namespace ImGui // Settings/.Ini Utilities // - The disk functions are automatically called if io.IniFilename != NULL (default is "imgui.ini"). // - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually. + // - Important: default value "imgui.ini" is relative to current working dir! Most apps will want to lock this to an absolute path (e.g. same path as executables). IMGUI_API void LoadIniSettingsFromDisk(const char* ini_filename); // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename). IMGUI_API void LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source. IMGUI_API void SaveIniSettingsToDisk(const char* ini_filename); // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext). @@ -904,7 +955,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoMove = 1 << 2, // Disable user moving the window ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programmatically) ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set. - ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it + ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it. Also referred to as Window Menu Button (e.g. within a docking node). ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame ImGuiWindowFlags_NoBackground = 1 << 7, // Disable drawing background color (WindowBg, etc.) and outside border. Similar as using SetNextWindowBgAlpha(0.0f). ImGuiWindowFlags_NoSavedSettings = 1 << 8, // Never load/save settings in .ini file @@ -918,13 +969,13 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) - ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. When used in a tab/docking context, tab is selected on closure and closure is deferred by one frame to allow code to cancel the closure (with a confirmation popup, etc.) without flicker. + ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, // [Internal] - ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) + ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] On child window: allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows. ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() @@ -958,10 +1009,7 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) ImGuiInputTextFlags_CallbackResize = 1 << 18, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) - ImGuiInputTextFlags_CallbackEdit = 1 << 19, // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) - // [Internal] - ImGuiInputTextFlags_Multiline = 1 << 20, // For internal use by InputTextMultiline() - ImGuiInputTextFlags_NoMarkEdited = 1 << 21 // For internal use by functions using InputText() before reformatting data + ImGuiInputTextFlags_CallbackEdit = 1 << 19 // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) // Obsolete names (will be removed soon) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -1059,7 +1107,7 @@ enum ImGuiTabBarFlags_ enum ImGuiTabItemFlags_ { ImGuiTabItemFlags_None = 0, - ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. Also: tab is selected on closure and closure is deferred by one frame to allow code to undo it without flicker. + ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Display a dot next to the title + tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. ImGuiTabItemFlags_SetSelected = 1 << 1, // Trigger flag to programmatically make the tab selected when calling BeginTabItem() ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. ImGuiTabItemFlags_NoPushId = 1 << 3, // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() @@ -1153,28 +1201,30 @@ enum ImGuiTableColumnFlags_ { // Input configuration flags ImGuiTableColumnFlags_None = 0, - ImGuiTableColumnFlags_DefaultHide = 1 << 0, // Default as a hidden/disabled column. - ImGuiTableColumnFlags_DefaultSort = 1 << 1, // Default as a sorting column. - ImGuiTableColumnFlags_WidthStretch = 1 << 2, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp). - ImGuiTableColumnFlags_WidthFixed = 1 << 3, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable). - ImGuiTableColumnFlags_NoResize = 1 << 4, // Disable manual resizing. - ImGuiTableColumnFlags_NoReorder = 1 << 5, // Disable manual reordering this column, this will also prevent other columns from crossing over this column. - ImGuiTableColumnFlags_NoHide = 1 << 6, // Disable ability to hide/disable this column. - ImGuiTableColumnFlags_NoClip = 1 << 7, // Disable clipping for this column (all NoClip columns will render in a same draw command). - ImGuiTableColumnFlags_NoSort = 1 << 8, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). - ImGuiTableColumnFlags_NoSortAscending = 1 << 9, // Disable ability to sort in the ascending direction. - ImGuiTableColumnFlags_NoSortDescending = 1 << 10, // Disable ability to sort in the descending direction. - ImGuiTableColumnFlags_NoHeaderWidth = 1 << 11, // Disable header text width contribution to automatic column width. - ImGuiTableColumnFlags_PreferSortAscending = 1 << 12, // Make the initial sort direction Ascending when first sorting on this column (default). - ImGuiTableColumnFlags_PreferSortDescending = 1 << 13, // Make the initial sort direction Descending when first sorting on this column. - ImGuiTableColumnFlags_IndentEnable = 1 << 14, // Use current Indent value when entering cell (default for column 0). - ImGuiTableColumnFlags_IndentDisable = 1 << 15, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored. + ImGuiTableColumnFlags_Disabled = 1 << 0, // Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state) + ImGuiTableColumnFlags_DefaultHide = 1 << 1, // Default as a hidden/disabled column. + ImGuiTableColumnFlags_DefaultSort = 1 << 2, // Default as a sorting column. + ImGuiTableColumnFlags_WidthStretch = 1 << 3, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp). + ImGuiTableColumnFlags_WidthFixed = 1 << 4, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable). + ImGuiTableColumnFlags_NoResize = 1 << 5, // Disable manual resizing. + ImGuiTableColumnFlags_NoReorder = 1 << 6, // Disable manual reordering this column, this will also prevent other columns from crossing over this column. + ImGuiTableColumnFlags_NoHide = 1 << 7, // Disable ability to hide/disable this column. + ImGuiTableColumnFlags_NoClip = 1 << 8, // Disable clipping for this column (all NoClip columns will render in a same draw command). + ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). + ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction. + ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction. + ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will not submit label for this column. Convenient for some small columns. Name will still appear in context menu. + ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width. + ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default). + ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column. + ImGuiTableColumnFlags_IndentEnable = 1 << 16, // Use current Indent value when entering cell (default for column 0). + ImGuiTableColumnFlags_IndentDisable = 1 << 17, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored. // Output status flags, read-only via TableGetColumnFlags() - ImGuiTableColumnFlags_IsEnabled = 1 << 20, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags. - ImGuiTableColumnFlags_IsVisible = 1 << 21, // Status: is visible == is enabled AND not clipped by scrolling. - ImGuiTableColumnFlags_IsSorted = 1 << 22, // Status: is currently part of the sort specs - ImGuiTableColumnFlags_IsHovered = 1 << 23, // Status: is hovered by mouse + ImGuiTableColumnFlags_IsEnabled = 1 << 24, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags. + ImGuiTableColumnFlags_IsVisible = 1 << 25, // Status: is visible == is enabled AND not clipped by scrolling. + ImGuiTableColumnFlags_IsSorted = 1 << 26, // Status: is currently part of the sort specs + ImGuiTableColumnFlags_IsHovered = 1 << 27, // Status: is hovered by mouse // [Internal] Combinations and masks ImGuiTableColumnFlags_WidthMask_ = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed, @@ -1216,9 +1266,11 @@ enum ImGuiTableBgTarget_ enum ImGuiFocusedFlags_ { ImGuiFocusedFlags_None = 0, - ImGuiFocusedFlags_ChildWindows = 1 << 0, // IsWindowFocused(): Return true if any children of the window is focused - ImGuiFocusedFlags_RootWindow = 1 << 1, // IsWindowFocused(): Test from root window (top most parent of the current hierarchy) - ImGuiFocusedFlags_AnyWindow = 1 << 2, // IsWindowFocused(): Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! + ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused + ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) + ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! + ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3, // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + //ImGuiFocusedFlags_DockHierarchy = 1 << 4, // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows }; @@ -1231,11 +1283,13 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered - ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 3, // Return true even if a popup window is normally blocking access to this item/window - //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. - ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. - ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is obstructed or overlapped by another window - ImGuiHoveredFlags_AllowWhenDisabled = 1 << 7, // Return true even if the item is disabled + ImGuiHoveredFlags_NoPopupHierarchy = 1 << 3, // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) + //ImGuiHoveredFlags_DockHierarchy = 1 << 4, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) + ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, // Return true even if a popup window is normally blocking access to this item/window + //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 6, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. + ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. + ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 8, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window + ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // IsItemHovered() only: Return true even if the item is disabled ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows }; @@ -1361,13 +1415,12 @@ enum ImGuiNavInput_ // [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them. // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeysDown[] instead of io.NavInputs[]. - ImGuiNavInput_KeyMenu_, // toggle menu // = io.KeyAlt ImGuiNavInput_KeyLeft_, // move left // = Arrow keys ImGuiNavInput_KeyRight_, // move right ImGuiNavInput_KeyUp_, // move up ImGuiNavInput_KeyDown_, // move down ImGuiNavInput_COUNT, - ImGuiNavInput_InternalStart_ = ImGuiNavInput_KeyMenu_ + ImGuiNavInput_InternalStart_ = ImGuiNavInput_KeyLeft_ }; // Configuration flags stored in io.ConfigFlags. Set by user/application. @@ -1466,6 +1519,7 @@ enum ImGuiStyleVar_ { // Enum name --------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions) ImGuiStyleVar_Alpha, // float Alpha + ImGuiStyleVar_DisabledAlpha, // float DisabledAlpha ImGuiStyleVar_WindowPadding, // ImVec2 WindowPadding ImGuiStyleVar_WindowRounding, // float WindowRounding ImGuiStyleVar_WindowBorderSize, // float WindowBorderSize @@ -1537,13 +1591,13 @@ enum ImGuiColorEditFlags_ // Defaults Options. You can set application defaults using SetColorEditOptions(). The intent is that you probably don't want to // override them in most of your calls. Let the user choose via the option menu and/or call SetColorEditOptions() once during startup. - ImGuiColorEditFlags__OptionsDefault = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, + ImGuiColorEditFlags_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, // [Internal] Masks - ImGuiColorEditFlags__DisplayMask = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, - ImGuiColorEditFlags__DataTypeMask = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, - ImGuiColorEditFlags__PickerMask = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, - ImGuiColorEditFlags__InputMask = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV + ImGuiColorEditFlags_DisplayMask_ = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, + ImGuiColorEditFlags_DataTypeMask_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, + ImGuiColorEditFlags_PickerMask_ = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, + ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -1637,6 +1691,7 @@ template void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p // Do NOT use this class as a std::vector replacement in your own code! Many of the structures used by dear imgui can be safely initialized by a zero-memset. //----------------------------------------------------------------------------- +IM_MSVC_RUNTIME_CHECKS_OFF template struct ImVector { @@ -1653,7 +1708,11 @@ struct ImVector inline ImVector() { Size = Capacity = 0; Data = NULL; } inline ImVector(const ImVector& src) { Size = Capacity = 0; Data = NULL; operator=(src); } inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; } - inline ~ImVector() { if (Data) IM_FREE(Data); } + inline ~ImVector() { if (Data) IM_FREE(Data); } // Important: does not destruct anything + + inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } // Important: does not destruct anything + inline void clear_delete() { for (int n = 0; n < Size; n++) IM_DELETE(Data[n]); clear(); } // Important: never called automatically! always explicit. + inline void clear_destruct() { for (int n = 0; n < Size; n++) Data[n].~T(); clear(); } // Important: never called automatically! always explicit. inline bool empty() const { return Size == 0; } inline int size() const { return Size; } @@ -1663,7 +1722,6 @@ struct ImVector inline T& operator[](int i) { IM_ASSERT(i >= 0 && i < Size); return Data[i]; } inline const T& operator[](int i) const { IM_ASSERT(i >= 0 && i < Size); return Data[i]; } - inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } inline T* begin() { return Data; } inline const T* begin() const { return Data; } inline T* end() { return Data + Size; } @@ -1685,7 +1743,7 @@ struct ImVector inline void pop_back() { IM_ASSERT(Size > 0); Size--; } inline void push_front(const T& v) { if (Size == 0) push_back(v); else insert(Data, v); } inline T* erase(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; } - inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last > it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - count) * sizeof(T)); Size -= (int)count; return Data + off; } + inline T* erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last > it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - (size_t)count) * sizeof(T)); Size -= (int)count; return Data + off; } inline T* erase_unsorted(const T* it) { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; if (it < Data + Size - 1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; } inline T* insert(const T* it, const T& v) { IM_ASSERT(it >= Data && it <= Data + Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; } inline bool contains(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; } @@ -1695,6 +1753,7 @@ struct ImVector inline bool find_erase_unsorted(const T& v) { const T* it = find(v); if (it < Data + Size) { erase_unsorted(it); return true; } return false; } inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; return (int)off; } }; +IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- // [SECTION] ImGuiStyle @@ -1707,6 +1766,7 @@ struct ImVector struct ImGuiStyle { float Alpha; // Global alpha applies to everything in Dear ImGui. + float DisabledAlpha; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. ImVec2 WindowPadding; // Padding within a window. float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. float WindowBorderSize; // Thickness of border around windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). @@ -1769,7 +1829,7 @@ struct ImGuiIO ImVec2 DisplaySize; // // Main display size, in pixels (generally == GetMainViewport()->Size) float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. - const char* IniFilename; // = "imgui.ini" // Path to .ini file. Set NULL to disable automatic .ini loading/saving, if e.g. you want to manually load/save from memory. + const char* IniFilename; // = "imgui.ini" // Path to .ini file (important: default "imgui.ini" is relative to current working dir!). Set NULL to disable automatic .ini loading/saving or if you want to manually call LoadIniSettingsXXX() / SaveIniSettingsXXX() functions. const char* LogFilename; // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified). float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. @@ -1836,7 +1896,9 @@ struct ImGuiIO IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue new characters input from an UTF-8 string - IMGUI_API void ClearInputCharacters(); // Clear the text input buffer manually + IMGUI_API void AddFocusEvent(bool focused); // Notifies Dear ImGui when hosting platform windows lose or gain input focus + IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually + IMGUI_API void ClearInputKeys(); // [Internal] Release all keys //------------------------------------------------------------------ // Output - Updated by NewFrame() or EndFrame()/Render() @@ -1851,7 +1913,7 @@ struct ImGuiIO bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving! bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events). - float Framerate; // Application framerate estimate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. + float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. int MetricsRenderVertices; // Vertices output during last call to Render() int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 int MetricsRenderWindows; // Number of visible windows @@ -1863,15 +1925,19 @@ struct ImGuiIO // [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed! //------------------------------------------------------------------ + bool WantCaptureMouseUnlessPopupClose;// Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup. ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() + ImGuiKeyModFlags KeyModsPrev; // Previous key mods ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) ImVec2 MouseClickedPos[5]; // Position at time of clicking double MouseClickedTime[5]; // Time of last click (used to figure out double-click) - bool MouseClicked[5]; // Mouse button went from !Down to Down - bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? + bool MouseClicked[5]; // Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0) + bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2) + ImU16 MouseClickedCount[5]; // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down + ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done. bool MouseReleased[5]; // Mouse button went from Down to !Down - bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window. We don't request mouse capture from the application if click started outside ImGui bounds. - bool MouseDownWasDoubleClick[5]; // Track if button down was a double-click + bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds. + bool MouseDownOwnedUnlessPopupClose[5];//Track if button was clicked inside a dear imgui window. float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) float MouseDownDurationPrev[5]; // Previous time the mouse button has been down ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point @@ -1881,6 +1947,7 @@ struct ImGuiIO float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. + bool AppFocusLost; ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16 ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. @@ -2105,10 +2172,12 @@ struct ImGuiStorage }; // Helper: Manually clip large list of items. -// If you are submitting lots of evenly spaced items and you have a random access to the list, you can perform coarse -// clipping based on visibility to save yourself from processing those items at all. +// If you have lots evenly spaced items and you have a random access to the list, you can perform coarse +// clipping based on visibility to only submit items that are in view. // The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. -// (Dear ImGui already clip items based on their bounds but it needs to measure text size to do so, whereas manual coarse clipping before submission makes this cost and your own data fetching/submission cost almost null) +// (Dear ImGui already clip items based on their bounds but: it needs to first layout the item to do so, and generally +// fetching/submitting your own data incurs additional cost. Coarse clipping using ImGuiListClipper allows you to easily +// scale using lists with tens of thousands of items without a problem) // Usage: // ImGuiListClipper clipper; // clipper.Begin(1000); // We have 1000 elements, evenly spaced. @@ -2117,30 +2186,30 @@ struct ImGuiStorage // ImGui::Text("line number %d", i); // Generally what happens is: // - Clipper lets you process the first element (DisplayStart = 0, DisplayEnd = 1) regardless of it being visible or not. -// - User code submit one element. +// - User code submit that one element. // - Clipper can measure the height of the first element // - Clipper calculate the actual range of elements to display based on the current clipping rectangle, position the cursor before the first visible element. // - User code submit visible elements. +// - The clipper also handles various subtleties related to keyboard/gamepad navigation, wrapping etc. struct ImGuiListClipper { - int DisplayStart; - int DisplayEnd; - - // [Internal] - int ItemsCount; - int StepNo; - int ItemsFrozen; - float ItemsHeight; - float StartPosY; - - IMGUI_API ImGuiListClipper(); - IMGUI_API ~ImGuiListClipper(); + int DisplayStart; // First item to display, updated by each call to Step() + int DisplayEnd; // End of items to display (exclusive) + int ItemsCount; // [Internal] Number of items + float ItemsHeight; // [Internal] Height of item after a first step and item submission can calculate it + float StartPosY; // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed + void* TempData; // [Internal] Internal data // items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step) // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). - IMGUI_API void Begin(int items_count, float items_height = -1.0f); // Automatically called by constructor if you passed 'items_count' or by Step() in Step 1. - IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. - IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. + IMGUI_API ImGuiListClipper(); + IMGUI_API ~ImGuiListClipper(); + IMGUI_API void Begin(int items_count, float items_height = -1.0f); + IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. + IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. + + // Call ForceDisplayRangeByIndices() before first call to Step() if you need a range of items to be displayed regardless of visibility. + IMGUI_API void ForceDisplayRangeByIndices(int item_min, int item_max); // item_max is exclusive e.g. use (42, 42+1) to make item 42 always visible BUT due to alignment/padding of certain items it is likely that an extra item may be included on either end of the display range. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset(this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79] @@ -2230,14 +2299,10 @@ struct ImDrawCmd void* UserCallbackData; // 4-8 // The draw callback code can access this. ImDrawCmd() { memset(this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed -}; -// Vertex index, default to 16-bit -// To allow large meshes with 16-bit indices: set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset in the renderer backend (recommended). -// To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in imconfig.h. -#ifndef ImDrawIdx -typedef unsigned short ImDrawIdx; -#endif + // Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature) + inline ImTextureID GetTexID() const { return TextureId; } +}; // Vertex layout #ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT @@ -2442,6 +2507,7 @@ struct ImDrawList IMGUI_API void _ResetForNewFrame(); IMGUI_API void _ClearFreeMemory(); IMGUI_API void _PopUnusedDrawCmd(); + IMGUI_API void _TryMergeDrawCmds(); IMGUI_API void _OnChangedClipRect(); IMGUI_API void _OnChangedTextureID(); IMGUI_API void _OnChangedVtxOffset(); @@ -2592,7 +2658,7 @@ struct ImFontAtlas IMGUI_API bool Build(); // Build pixels data. This is called automatically for you by the GetTexData*** functions. IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel - bool IsBuilt() const { return Fonts.Size > 0 && (TexPixelsAlpha8 != NULL || TexPixelsRGBA32 != NULL); } + bool IsBuilt() const { return Fonts.Size > 0 && TexReady; } // Bit ambiguous: used to detect when user didn't built texture but effectively we should check TexID != 0 except that would be backend dependent... void SetTexID(ImTextureID id) { TexID = id; } //------------------------------------------- @@ -2642,6 +2708,7 @@ struct ImFontAtlas // [Internal] // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. + bool TexReady; // Set when texture was built matching current font input bool TexPixelsUseColors; // Tell whether our texture data is known to use colors (rather than just alpha channel), in order to help backend select a format. unsigned char* TexPixelsAlpha8; // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight unsigned int* TexPixelsRGBA32; // 4 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight * 4 @@ -2664,7 +2731,7 @@ struct ImFontAtlas #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+ - typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ + //typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+ #endif }; @@ -2686,8 +2753,9 @@ struct ImFont ImFontAtlas* ContainerAtlas; // 4-8 // out // // What we has been loaded into const ImFontConfig* ConfigData; // 4-8 // in // // Pointer within ContainerAtlas->ConfigData short ConfigDataCount; // 2 // in // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. - ImWchar FallbackChar; // 2 // in // = '?' // Replacement character if a glyph isn't found. Only set via SetFallbackChar() - ImWchar EllipsisChar; // 2 // out // = -1 // Character used for ellipsis rendering. + ImWchar FallbackChar; // 2 // out // = FFFD/'?' // Character used if a glyph isn't found. + ImWchar EllipsisChar; // 2 // out // = '...' // Character used for ellipsis rendering. + ImWchar DotChar; // 2 // out // = '.' // Character used for ellipsis rendering (if a single '...' character isn't found) bool DirtyLookupTables; // 1 // out // float Scale; // 4 // in // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale() float Ascent, Descent; // 4+4 // out // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] @@ -2717,7 +2785,6 @@ struct ImFont IMGUI_API void AddGlyph(const ImFontConfig* src_cfg, ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x); IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built. IMGUI_API void SetGlyphVisible(ImWchar c, bool visible); - IMGUI_API void SetFallbackChar(ImWchar c); IMGUI_API bool IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last); }; @@ -2725,7 +2792,7 @@ struct ImFont // [SECTION] Viewports //----------------------------------------------------------------------------- -// Flags stored in ImGuiViewport::Flags +// Flags stored in ImGuiViewport::Flags, giving indications to the platform backends. enum ImGuiViewportFlags_ { ImGuiViewportFlags_None = 0, @@ -2765,6 +2832,10 @@ struct ImGuiViewport #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // OBSOLETED in 1.86 (from November 2021) + IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Calculate coarse clipping for large list of evenly sized items. Prefer using ImGuiListClipper. + // OBSOLETED in 1.85 (from August 2021) + static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; } // OBSOLETED in 1.81 (from February 2021) IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // Helper to calculate size from items_count and height_in_items static inline bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)) { return BeginListBox(label, size); } @@ -2794,8 +2865,18 @@ namespace ImGui static inline void SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); } // OBSOLETED in 1.70 (from May 2019) static inline float GetContentRegionAvailWidth() { return GetContentRegionAvail().x; } - // OBSOLETED in 1.69 (from Mar 2019) - static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } + + // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) + //static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.69 (from Mar 2019) + //static inline void SetScrollHere(float ratio = 0.5f) { SetScrollHereY(ratio); } // OBSOLETED in 1.66 (from Nov 2018) + //static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } // OBSOLETED in 1.63 (from Aug 2018) + //static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } // OBSOLETED in 1.60 (from Apr 2018) + //static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018) + //static inline void ShowTestWindow() { return ShowDemoWindow(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline bool IsRootWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline bool IsRootWindowOrAnyChildFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline void SetNextWindowContentWidth(float w) { SetNextWindowContentSize(ImVec2(w, 0.0f)); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline float GetItemsLineHeightWithSpacing() { return GetFrameHeightWithSpacing(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) } // OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect() @@ -2824,6 +2905,10 @@ enum ImDrawCornerFlags_ #pragma GCC diagnostic pop #endif +#ifdef _MSC_VER +#pragma warning (pop) +#endif + // Include imgui_user.h at the end of imgui.h (convenient for user to only explicitly include vanilla imgui.h) #ifdef IMGUI_INCLUDE_IMGUI_USER_H #include "imgui_user.h" diff --git a/external/imgui/include/imgui_impl_dx11.h b/r5dev/thirdparty/imgui/include/imgui_impl_dx11.h similarity index 81% rename from external/imgui/include/imgui_impl_dx11.h rename to r5dev/thirdparty/imgui/include/imgui_impl_dx11.h index 03fee14d..a83bce1b 100644 --- a/external/imgui/include/imgui_impl_dx11.h +++ b/r5dev/thirdparty/imgui/include/imgui_impl_dx11.h @@ -5,7 +5,8 @@ // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs diff --git a/external/imgui/include/imgui_impl_win32.h b/r5dev/thirdparty/imgui/include/imgui_impl_win32.h similarity index 91% rename from external/imgui/include/imgui_impl_win32.h rename to r5dev/thirdparty/imgui/include/imgui_impl_win32.h index 5197b7f8..768fe169 100644 --- a/external/imgui/include/imgui_impl_win32.h +++ b/r5dev/thirdparty/imgui/include/imgui_impl_win32.h @@ -7,7 +7,8 @@ // [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs diff --git a/external/imgui/include/imgui_internal.h b/r5dev/thirdparty/imgui/include/imgui_internal.h similarity index 84% rename from external/imgui/include/imgui_internal.h rename to r5dev/thirdparty/imgui/include/imgui_internal.h index a8c2b440..73d58f6b 100644 --- a/external/imgui/include/imgui_internal.h +++ b/r5dev/thirdparty/imgui/include/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.83 WIP +// dear imgui, v1.86 // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! @@ -18,12 +18,14 @@ Index of this file: // [SECTION] Generic helpers // [SECTION] ImDrawList support // [SECTION] Widgets support: flags, enums, data structures +// [SECTION] Clipper support +// [SECTION] Navigation support // [SECTION] Columns support // [SECTION] Multi-select support // [SECTION] Docking support // [SECTION] Viewport support // [SECTION] Settings support -// [SECTION] Metrics, Debug +// [SECTION] Metrics, Debug tools // [SECTION] Generic context hooks // [SECTION] ImGuiContext (main imgui context) // [SECTION] ImGuiWindowTempData, ImGuiWindow @@ -43,7 +45,7 @@ Index of this file: //----------------------------------------------------------------------------- #ifndef IMGUI_VERSION -#error Must include imgui.h before imgui_internal.h +#include "imgui.h" #endif #include // FILE*, sscanf @@ -51,10 +53,21 @@ Index of this file: #include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf #include // INT_MIN, INT_MAX +// Enable SSE intrinsics if available +#if (defined __SSE__ || defined __x86_64__ || defined _M_X64) && !defined(IMGUI_DISABLE_SSE) +#define IMGUI_ENABLE_SSE +#include +#endif + // Visual Studio warnings #ifdef _MSC_VER #pragma warning (push) -#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) +#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) +#pragma warning (disable: 26812) // The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) +#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). +#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later +#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types +#endif #endif // Clang/GCC warnings with -Weverything @@ -71,6 +84,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" #pragma clang diagnostic ignored "-Wdouble-promotion" #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#pragma clang diagnostic ignored "-Wmissing-noreturn" // warning: function 'xxx' could be declared with attribute 'noreturn' #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind @@ -105,9 +119,9 @@ struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box -struct ImGuiLastItemDataBackup; // Backup and restore IsItemHovered() internal data +struct ImGuiLastItemData; // Status storage for last submitted items struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only -struct ImGuiNavMoveResult; // Result of a gamepad/keyboard directional navigation move query result +struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions struct ImGuiNextWindowData; // Storage for SetNextWindow** functions struct ImGuiNextItemData; // Storage for SetNextItem** functions @@ -121,14 +135,16 @@ struct ImGuiTabBar; // Storage for a tab bar struct ImGuiTabItem; // Storage for a tab item (within a tab bar) struct ImGuiTable; // Storage for a table struct ImGuiTableColumn; // Storage for one column of a table +struct ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables. struct ImGuiTableSettings; // Storage for a table .ini settings struct ImGuiTableColumnsSettings; // Storage for a column .ini settings struct ImGuiWindow; // Storage for one window -struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame) +struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window) struct ImGuiWindowSettings; // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session) // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical +typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() @@ -137,6 +153,7 @@ typedef int ImGuiNavDirSourceFlags; // -> enum ImGuiNavDirSourceFlags_ // F typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests typedef int ImGuiNextItemDataFlags; // -> enum ImGuiNextItemDataFlags_ // Flags: for SetNextItemXXX() functions typedef int ImGuiNextWindowDataFlags; // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions +typedef int ImGuiScrollFlags; // -> enum ImGuiScrollFlags_ // Flags: for ScrollToItem() and navigation requests typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for SeparatorEx() typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx() typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx() @@ -228,13 +245,27 @@ namespace ImStb #define IMGUI_CDECL #endif +// Warnings +#if defined(_MSC_VER) && !defined(__clang__) +#define IM_MSVC_WARNING_SUPPRESS(XXXX) __pragma(warning(suppress: XXXX)) +#else +#define IM_MSVC_WARNING_SUPPRESS(XXXX) +#endif + // Debug Tools -// Use 'Metrics->Tools->Item Picker' to break into the call-stack of a specific item. +// Use 'Metrics/Debugger->Tools->Item Picker' to break into the call-stack of a specific item. +// This will call IM_DEBUG_BREAK() which you may redefine yourself. See https://github.com/scottt/debugbreak for more reference. #ifndef IM_DEBUG_BREAK -#if defined(__clang__) -#define IM_DEBUG_BREAK() __builtin_debugtrap() -#elif defined (_MSC_VER) +#if defined (_MSC_VER) #define IM_DEBUG_BREAK() __debugbreak() +#elif defined(__clang__) +#define IM_DEBUG_BREAK() __builtin_debugtrap() +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define IM_DEBUG_BREAK() __asm__ volatile("int $0x03") +#elif defined(__GNUC__) && defined(__thumb__) +#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xde01") +#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__) +#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0"); #else #define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger! #endif @@ -271,7 +302,9 @@ static inline ImGuiID ImHash(const void* data, int size, ImU32 seed = 0) { ret #endif // Helpers: Sorting -#define ImQsort qsort +#ifndef ImQsort +static inline void ImQsort(void* base, size_t count, size_t size_of_element, int(IMGUI_CDECL *compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); } +#endif // Helpers: Color Blending IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b); @@ -304,17 +337,19 @@ static inline bool ImCharIsBlankA(char c) { return c == ' ' || c = static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } // Helpers: UTF-8 <> wchar conversions -IMGUI_API int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count -IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // read one character. return input UTF-8 bytes count -IMGUI_API int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count -IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) -IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8 -IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 +IMGUI_API const char* ImTextCharToUtf8(char out_buf[5], unsigned int c); // return out_buf +IMGUI_API int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count +IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // read one character. return input UTF-8 bytes count +IMGUI_API int ImTextStrFromUtf8(ImWchar* out_buf, int out_buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count +IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) +IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8 +IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 // Helpers: ImVec2/ImVec4 operators // We are keeping those disabled by default so they don't leak in user space, to allow user enabling implicit cast operators between ImVec2 and their own types (using IM_VEC2_CLASS_EXTRA etc.) // We unfortunately don't have a unary- operator for ImVec2 because this would needs to be defined inside the class itself. #ifdef IMGUI_DEFINE_MATH_OPERATORS +IM_MSVC_RUNTIME_CHECKS_OFF static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x * rhs, lhs.y * rhs); } static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x / rhs, lhs.y / rhs); } static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); } @@ -330,6 +365,7 @@ static inline ImVec2& operator/=(ImVec2& lhs, const ImVec2& rhs) static inline ImVec4 operator+(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); } static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); } static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); } +IM_MSVC_RUNTIME_CHECKS_RESTORE #endif // Helpers: File System @@ -355,6 +391,7 @@ IMGUI_API ImU64 ImFileWrite(const void* data, ImU64 size, ImU64 coun IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size = NULL, int padding_bytes = 0); // Helpers: Maths +IM_MSVC_RUNTIME_CHECKS_OFF // - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy) #ifndef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS #define ImFabs(X) fabsf(X) @@ -374,8 +411,14 @@ static inline double ImLog(double x) { return log(x); } static inline int ImAbs(int x) { return x < 0 ? -x : x; } static inline float ImAbs(float x) { return fabsf(x); } static inline double ImAbs(double x) { return fabs(x); } -static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : ((x > 0.0f) ? 1.0f : 0.0f); } // Sign operator - returns -1, 0 or 1 based on sign of argument -static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : ((x > 0.0) ? 1.0 : 0.0); } +static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument +static inline double ImSign(double x) { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; } +#ifdef IMGUI_ENABLE_SSE +static inline float ImRsqrt(float x) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); } +#else +static inline float ImRsqrt(float x) { return 1.0f / sqrtf(x); } +#endif +static inline double ImRsqrt(double x) { return 1.0 / sqrt(x); } #endif // - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double // (Exceptionally using templates here but we could also redefine them for those types) @@ -396,7 +439,7 @@ static inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } static inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); } static inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); } -static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return 1.0f / ImSqrt(d); return fail_value; } +static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; } static inline float ImFloor(float f) { return (float)(int)(f); } static inline float ImFloorSigned(float f) { return (float)((f >= 0 || (int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); } @@ -405,6 +448,8 @@ static inline float ImDot(const ImVec2& a, const ImVec2& b) static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } +static inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; } +IM_MSVC_RUNTIME_CHECKS_RESTORE // Helpers: Geometry IMGUI_API ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t); @@ -420,6 +465,7 @@ IMGUI_API ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy); // Helper: ImVec1 (1D vector) // (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches) +IM_MSVC_RUNTIME_CHECKS_OFF struct ImVec1 { float x; @@ -473,6 +519,7 @@ struct IMGUI_API ImRect bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); } }; +IM_MSVC_RUNTIME_CHECKS_RESTORE // Helper: ImBitArray inline bool ImBitArrayTestBit(const ImU32* arr, int n) { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; } @@ -492,12 +539,12 @@ inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on ran } // Helper: ImBitArray class (wrapper over ImBitArray functions) -// Store 1-bit per value. NOT CLEARED by constructor. +// Store 1-bit per value. template struct IMGUI_API ImBitArray { ImU32 Storage[(BITCOUNT + 31) >> 5]; - ImBitArray() { } + ImBitArray() { ClearAllBits(); } void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); } void SetAllBits() { memset(Storage, 255, sizeof(Storage)); } bool TestBit(int n) const { IM_ASSERT(n < BITCOUNT); return ImBitArrayTestBit(Storage, n); } @@ -579,20 +626,30 @@ struct IMGUI_API ImPool ImVector Buf; // Contiguous data ImGuiStorage Map; // ID->Index ImPoolIdx FreeIdx; // Next free idx to use + ImPoolIdx AliveCount; // Number of active/alive items (for display purpose) - ImPool() { FreeIdx = 0; } + ImPool() { FreeIdx = AliveCount = 0; } ~ImPool() { Clear(); } T* GetByKey(ImGuiID key) { int idx = Map.GetInt(key, -1); return (idx != -1) ? &Buf[idx] : NULL; } T* GetByIndex(ImPoolIdx n) { return &Buf[n]; } ImPoolIdx GetIndex(const T* p) const { IM_ASSERT(p >= Buf.Data && p < Buf.Data + Buf.Size); return (ImPoolIdx)(p - Buf.Data); } T* GetOrAddByKey(ImGuiID key) { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Buf[*p_idx]; *p_idx = FreeIdx; return Add(); } bool Contains(const T* p) const { return (p >= Buf.Data && p < Buf.Data + Buf.Size); } - void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = 0; } - T* Add() { int idx = FreeIdx; if (idx == Buf.Size) { Buf.resize(Buf.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Buf[idx]; } IM_PLACEMENT_NEW(&Buf[idx]) T(); return &Buf[idx]; } + void Clear() { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = AliveCount = 0; } + T* Add() { int idx = FreeIdx; if (idx == Buf.Size) { Buf.resize(Buf.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Buf[idx]; } IM_PLACEMENT_NEW(&Buf[idx]) T(); AliveCount++; return &Buf[idx]; } void Remove(ImGuiID key, const T* p) { Remove(key, GetIndex(p)); } - void Remove(ImGuiID key, ImPoolIdx idx) { Buf[idx].~T(); *(int*)&Buf[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); } + void Remove(ImGuiID key, ImPoolIdx idx) { Buf[idx].~T(); *(int*)&Buf[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); AliveCount--; } void Reserve(int capacity) { Buf.reserve(capacity); Map.Data.reserve(capacity); } - int GetSize() const { return Buf.Size; } + + // To iterate a ImPool: for (int n = 0; n < pool.GetMapSize(); n++) if (T* t = pool.TryGetMapData(n)) { ... } + // Can be avoided if you know .Remove() has never been called on the pool, or AliveCount == GetMapSize() + int GetAliveCount() const { return AliveCount; } // Number of active/alive items in the pool (for display purpose) + int GetBufSize() const { return Buf.Size; } + int GetMapSize() const { return Map.Data.Size; } // It is the map we need iterate to find valid items, since we don't have "alive" storage anywhere + T* TryGetMapData(ImPoolIdx n) { int idx = Map.Data[n].val_i; if (idx == -1) return NULL; return GetByIndex(idx); } +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + int GetSize() { return GetMapSize(); } // For ImPlot: should use GetMapSize() from (IMGUI_VERSION_NUM >= 18304) +#endif }; // Helper: ImChunkStream<> @@ -690,39 +747,49 @@ struct ImDrawDataBuilder enum ImGuiItemFlags_ { ImGuiItemFlags_None = 0, - ImGuiItemFlags_NoTabStop = 1 << 0, // false - ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. - ImGuiItemFlags_Disabled = 1 << 2, // false // [BETA] Disable interactions but doesn't affect visuals yet. See github.com/ocornut/imgui/issues/211 - ImGuiItemFlags_NoNav = 1 << 3, // false - ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false - ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window - ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) - ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. - ImGuiItemFlags_Default_ = 0 + ImGuiItemFlags_NoTabStop = 1 << 0, // false // Disable keyboard tabbing (FIXME: should merge with _NoNav) + ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. + ImGuiItemFlags_Disabled = 1 << 2, // false // Disable interactions but doesn't affect visuals. See BeginDisabled()/EndDisabled(). See github.com/ocornut/imgui/issues/211 + ImGuiItemFlags_NoNav = 1 << 3, // false // Disable keyboard/gamepad directional navigation (FIXME: should merge with _NoTabStop) + ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false // Disable item being a candidate for default focus (e.g. used by title bar items) + ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window + ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) + ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. + ImGuiItemFlags_Inputable = 1 << 8 // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. }; // Storage for LastItem data enum ImGuiItemStatusFlags_ { ImGuiItemStatusFlags_None = 0, - ImGuiItemStatusFlags_HoveredRect = 1 << 0, - ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // LastItemDisplayRect is valid + ImGuiItemStatusFlags_HoveredRect = 1 << 0, // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test) + ImGuiItemStatusFlags_HasDisplayRect = 1 << 1, // g.LastItemData.DisplayRect is valid ImGuiItemStatusFlags_Edited = 1 << 2, // Value exposed by item was edited in the current frame (should match the bool return value of most widgets) - ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected" because reporting the change allows us to handle clipping with less issues. + ImGuiItemStatusFlags_ToggledSelection = 1 << 3, // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected", only state changes, in order to easily handle clipping with less issues. ImGuiItemStatusFlags_ToggledOpen = 1 << 4, // Set when TreeNode() reports toggling their open state. ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag. ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. - ImGuiItemStatusFlags_HoveredWindow = 1 << 7 // Override the HoveredWindow test to allow cross-window hover testing. + ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing. + ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8 // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon) #ifdef IMGUI_ENABLE_TEST_ENGINE , // [imgui_tests only] - ImGuiItemStatusFlags_Openable = 1 << 10, // - ImGuiItemStatusFlags_Opened = 1 << 11, // - ImGuiItemStatusFlags_Checkable = 1 << 12, // - ImGuiItemStatusFlags_Checked = 1 << 13 // + ImGuiItemStatusFlags_Openable = 1 << 20, // + ImGuiItemStatusFlags_Opened = 1 << 21, // + ImGuiItemStatusFlags_Checkable = 1 << 22, // + ImGuiItemStatusFlags_Checked = 1 << 23 // #endif }; +// Extend ImGuiInputTextFlags_ +enum ImGuiInputTextFlagsPrivate_ +{ + // [Internal] + ImGuiInputTextFlags_Multiline = 1 << 26, // For internal use by InputTextMultiline() + ImGuiInputTextFlags_NoMarkEdited = 1 << 27, // For internal use by functions using InputText() before reformatting data + ImGuiInputTextFlags_MergedItem = 1 << 28 // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match. +}; + // Extend ImGuiButtonFlags_ enum ImGuiButtonFlagsPrivate_ { @@ -736,7 +803,7 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_FlattenChildren = 1 << 11, // allow interactions even if a child window is overlapping ImGuiButtonFlags_AllowItemOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable, use along with SetItemAllowOverlap() ImGuiButtonFlags_DontClosePopups = 1 << 13, // disable automatically closing parent popup on press // [UNUSED] - ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions + //ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine ImGuiButtonFlags_NoKeyModifiers = 1 << 16, // disable mouse interaction if a key modifier is held ImGuiButtonFlags_NoHoldingActiveId = 1 << 17, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only) @@ -746,6 +813,12 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease }; +// Extend ImGuiComboFlags_ +enum ImGuiComboFlagsPrivate_ +{ + ImGuiComboFlags_CustomPreview = 1 << 20 // enable BeginComboPreview() +}; + // Extend ImGuiSliderFlags_ enum ImGuiSliderFlagsPrivate_ { @@ -758,12 +831,13 @@ enum ImGuiSelectableFlagsPrivate_ { // NB: need to be in sync with last value of ImGuiSelectableFlags_ ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20, - ImGuiSelectableFlags_SelectOnClick = 1 << 21, // Override button behavior to react on Click (default is Click+Release) - ImGuiSelectableFlags_SelectOnRelease = 1 << 22, // Override button behavior to react on Release (default is Click+Release) - ImGuiSelectableFlags_SpanAvailWidth = 1 << 23, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) - ImGuiSelectableFlags_DrawHoveredWhenHeld = 1 << 24, // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow. - ImGuiSelectableFlags_SetNavIdOnHover = 1 << 25, // Set Nav/Focus ID on mouse hover (used by MenuItem) - ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 26 // Disable padding each side with ItemSpacing * 0.5f + ImGuiSelectableFlags_SelectOnNav = 1 << 21, // (WIP) Auto-select when moved into. This is not exposed in public API as to handle multi-select and modifiers we will need user to explicitly control focus scope. May be replaced with a BeginSelection() API. + ImGuiSelectableFlags_SelectOnClick = 1 << 22, // Override button behavior to react on Click (default is Click+Release) + ImGuiSelectableFlags_SelectOnRelease = 1 << 23, // Override button behavior to react on Release (default is Click+Release) + ImGuiSelectableFlags_SpanAvailWidth = 1 << 24, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) + ImGuiSelectableFlags_DrawHoveredWhenHeld = 1 << 25, // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow. + ImGuiSelectableFlags_SetNavIdOnHover = 1 << 26, // Set Nav/Focus ID on mouse hover (used by MenuItem) + ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 27 // Disable padding each side with ItemSpacing * 0.5f }; // Extend ImGuiTreeNodeFlags_ @@ -845,49 +919,6 @@ enum ImGuiInputReadMode ImGuiInputReadMode_RepeatFast }; -enum ImGuiNavHighlightFlags_ -{ - ImGuiNavHighlightFlags_None = 0, - ImGuiNavHighlightFlags_TypeDefault = 1 << 0, - ImGuiNavHighlightFlags_TypeThin = 1 << 1, - ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse. - ImGuiNavHighlightFlags_NoRounding = 1 << 3 -}; - -enum ImGuiNavDirSourceFlags_ -{ - ImGuiNavDirSourceFlags_None = 0, - ImGuiNavDirSourceFlags_Keyboard = 1 << 0, - ImGuiNavDirSourceFlags_PadDPad = 1 << 1, - ImGuiNavDirSourceFlags_PadLStick = 1 << 2 -}; - -enum ImGuiNavMoveFlags_ -{ - ImGuiNavMoveFlags_None = 0, - ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side - ImGuiNavMoveFlags_LoopY = 1 << 1, - ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) - ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful for provided for completeness - ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) - ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisibleSet that only comprise elements that are already fully visible. - ImGuiNavMoveFlags_ScrollToEdge = 1 << 6 -}; - -enum ImGuiNavForward -{ - ImGuiNavForward_None, - ImGuiNavForward_ForwardQueued, - ImGuiNavForward_ForwardActive -}; - -enum ImGuiNavLayer -{ - ImGuiNavLayer_Main = 0, // Main scrolling layer - ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu) - ImGuiNavLayer_COUNT -}; - enum ImGuiPopupPositionPolicy { ImGuiPopupPositionPolicy_Default, @@ -934,6 +965,19 @@ struct ImGuiStyleMod ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; } }; +// Storage data for BeginComboPreview()/EndComboPreview() +struct IMGUI_API ImGuiComboPreviewData +{ + ImRect PreviewRect; + ImVec2 BackupCursorPos; + ImVec2 BackupCursorMaxPos; + ImVec2 BackupCursorPosPrevLine; + float BackupPrevLineTextBaseOffset; + ImGuiLayoutType BackupLayout; + + ImGuiComboPreviewData() { memset(this, 0, sizeof(*this)); } +}; + // Stacked storage data for BeginGroup()/EndGroup() struct IMGUI_API ImGuiGroupData { @@ -953,14 +997,19 @@ struct IMGUI_API ImGuiGroupData // Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper. struct IMGUI_API ImGuiMenuColumns { - float Spacing; - float Width, NextWidth; - float Pos[3], NextWidths[3]; + ImU32 TotalWidth; + ImU32 NextTotalWidth; + ImU16 Spacing; + ImU16 OffsetIcon; // Always zero for now + ImU16 OffsetLabel; // Offsets are locked in Update() + ImU16 OffsetShortcut; + ImU16 OffsetMark; + ImU16 Widths[4]; // Width of: Icon, Label, Shortcut, Mark (accumulators for current frame) ImGuiMenuColumns() { memset(this, 0, sizeof(*this)); } - void Update(int count, float spacing, bool clear); - float DeclColumns(float w0, float w1, float w2); - float CalcExtraSpace(float avail_w) const; + void Update(float spacing, bool window_reappearing); + float DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark); + void CalcNextTotalWidth(bool update_offsets); }; // Internal state of the currently focused/edited text input box @@ -980,9 +1029,7 @@ struct IMGUI_API ImGuiInputTextState bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool Edited; // edited this frame - ImGuiInputTextFlags UserFlags; // Temporarily set while we call user's callback - ImGuiInputTextCallback UserCallback; // " - void* UserCallbackData; // " + ImGuiInputTextFlags Flags; // copy of InputText() flags ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); } @@ -996,6 +1043,9 @@ struct IMGUI_API ImGuiInputTextState void CursorClamp() { Stb.cursor = ImMin(Stb.cursor, CurLenW); Stb.select_start = ImMin(Stb.select_start, CurLenW); Stb.select_end = ImMin(Stb.select_end, CurLenW); } bool HasSelection() const { return Stb.select_start != Stb.select_end; } void ClearSelection() { Stb.select_start = Stb.select_end = Stb.cursor; } + int GetCursorPos() const { return Stb.cursor; } + int GetSelectionStart() const { return Stb.select_start; } + int GetSelectionEnd() const { return Stb.select_end; } void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; } }; @@ -1013,20 +1063,6 @@ struct ImGuiPopupData ImGuiPopupData() { memset(this, 0, sizeof(*this)); OpenFrameCount = -1; } }; -struct ImGuiNavMoveResult -{ - ImGuiWindow* Window; // Best candidate window - ImGuiID ID; // Best candidate ID - ImGuiID FocusScopeId; // Best candidate focus scope ID - float DistBox; // Best candidate box distance to current NavId - float DistCenter; // Best candidate center distance to current NavId - float DistAxial; - ImRect RectRel; // Best candidate bounding box in window relative space - - ImGuiNavMoveResult() { Clear(); } - void Clear() { Window = NULL; ID = FocusScopeId = 0; DistBox = DistCenter = DistAxial = FLT_MAX; RectRel = ImRect(); } -}; - enum ImGuiNextWindowDataFlags_ { ImGuiNextWindowDataFlags_None = 0, @@ -1057,7 +1093,7 @@ struct ImGuiNextWindowData ImGuiSizeCallback SizeCallback; void* SizeCallbackUserData; float BgAlphaVal; // Override background alpha - ImVec2 MenuBarOffsetMinVal; // *Always on* This is not exposed publicly, so we don't clear it. + ImVec2 MenuBarOffsetMinVal; // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?) ImGuiNextWindowData() { memset(this, 0, sizeof(*this)); } inline void ClearFlags() { Flags = ImGuiNextWindowDataFlags_None; } @@ -1082,6 +1118,44 @@ struct ImGuiNextItemData inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; } // Also cleared manually by ItemAdd()! }; +// Status storage for the last submitted item +struct ImGuiLastItemData +{ + ImGuiID ID; + ImGuiItemFlags InFlags; // See ImGuiItemFlags_ + ImGuiItemStatusFlags StatusFlags; // See ImGuiItemStatusFlags_ + ImRect Rect; // Full rectangle + ImRect NavRect; // Navigation scoring rectangle (not displayed) + ImRect DisplayRect; // Display rectangle (only if ImGuiItemStatusFlags_HasDisplayRect is set) + + ImGuiLastItemData() { memset(this, 0, sizeof(*this)); } +}; + +struct IMGUI_API ImGuiStackSizes +{ + short SizeOfIDStack; + short SizeOfColorStack; + short SizeOfStyleVarStack; + short SizeOfFontStack; + short SizeOfFocusScopeStack; + short SizeOfGroupStack; + short SizeOfItemFlagsStack; + short SizeOfBeginPopupStack; + short SizeOfDisabledStack; + + ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } + void SetToCurrentState(); + void CompareWithCurrentState(); +}; + +// Data saved for each window pushed into the stack +struct ImGuiWindowStackData +{ + ImGuiWindow* Window; + ImGuiLastItemData ParentLastItemDataBackup; + ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting +}; + struct ImGuiShrinkWidthItem { int Index; @@ -1097,6 +1171,120 @@ struct ImGuiPtrOrIndex ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; } }; +//----------------------------------------------------------------------------- +// [SECTION] Clipper support +//----------------------------------------------------------------------------- + +struct ImGuiListClipperRange +{ + int Min; + int Max; + bool PosToIndexConvert; // Begin/End are absolute position (will be converted to indices later) + ImS8 PosToIndexOffsetMin; // Add to Min after converting to indices + ImS8 PosToIndexOffsetMax; // Add to Min after converting to indices + + static ImGuiListClipperRange FromIndices(int min, int max) { ImGuiListClipperRange r = { min, max, false, 0, 0 }; return r; } + static ImGuiListClipperRange FromPositions(float y1, float y2, int off_min, int off_max) { ImGuiListClipperRange r = { (int)y1, (int)y2, true, (ImS8)off_min, (ImS8)off_max }; return r; } +}; + +// Temporary clipper data, buffers shared/reused between instances +struct ImGuiListClipperData +{ + ImGuiListClipper* ListClipper; + float LossynessOffset; + int StepNo; + int ItemsFrozen; + ImVector Ranges; + + ImGuiListClipperData() { memset(this, 0, sizeof(*this)); } + void Reset(ImGuiListClipper* clipper) { ListClipper = clipper; StepNo = ItemsFrozen = 0; Ranges.resize(0); } +}; + +//----------------------------------------------------------------------------- +// [SECTION] Navigation support +//----------------------------------------------------------------------------- + +enum ImGuiActivateFlags_ +{ + ImGuiActivateFlags_None = 0, + ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default if keyboard is available. + ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default if keyboard is not available. + ImGuiActivateFlags_TryToPreserveState = 1 << 2 // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection) +}; + +// Early work-in-progress API for ScrollToItem() +enum ImGuiScrollFlags_ +{ + ImGuiScrollFlags_None = 0, + ImGuiScrollFlags_KeepVisibleEdgeX = 1 << 0, // If item is not visible: scroll as little as possible on X axis to bring item back into view [default for X axis] + ImGuiScrollFlags_KeepVisibleEdgeY = 1 << 1, // If item is not visible: scroll as little as possible on Y axis to bring item back into view [default for Y axis for windows that are already visible] + ImGuiScrollFlags_KeepVisibleCenterX = 1 << 2, // If item is not visible: scroll to make the item centered on X axis [rarely used] + ImGuiScrollFlags_KeepVisibleCenterY = 1 << 3, // If item is not visible: scroll to make the item centered on Y axis + ImGuiScrollFlags_AlwaysCenterX = 1 << 4, // Always center the result item on X axis [rarely used] + ImGuiScrollFlags_AlwaysCenterY = 1 << 5, // Always center the result item on Y axis [default for Y axis for appearing window) + ImGuiScrollFlags_NoScrollParent = 1 << 6, // Disable forwarding scrolling to parent window if required to keep item/rect visible (only scroll window the function was applied to). + ImGuiScrollFlags_MaskX_ = ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleCenterX | ImGuiScrollFlags_AlwaysCenterX, + ImGuiScrollFlags_MaskY_ = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY +}; + +enum ImGuiNavHighlightFlags_ +{ + ImGuiNavHighlightFlags_None = 0, + ImGuiNavHighlightFlags_TypeDefault = 1 << 0, + ImGuiNavHighlightFlags_TypeThin = 1 << 1, + ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse. + ImGuiNavHighlightFlags_NoRounding = 1 << 3 +}; + +enum ImGuiNavDirSourceFlags_ +{ + ImGuiNavDirSourceFlags_None = 0, + ImGuiNavDirSourceFlags_RawKeyboard = 1 << 0, // Raw keyboard (not pulled from nav), faciliate use of some functions before we can unify nav and keys + ImGuiNavDirSourceFlags_Keyboard = 1 << 1, + ImGuiNavDirSourceFlags_PadDPad = 1 << 2, + ImGuiNavDirSourceFlags_PadLStick = 1 << 3 +}; + +enum ImGuiNavMoveFlags_ +{ + ImGuiNavMoveFlags_None = 0, + ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side + ImGuiNavMoveFlags_LoopY = 1 << 1, + ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) + ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful but provided for completeness + ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) + ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown) + ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary + ImGuiNavMoveFlags_Forwarded = 1 << 7, + ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result + ImGuiNavMoveFlags_FocusApi = 1 << 9, + ImGuiNavMoveFlags_Tabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight + ImGuiNavMoveFlags_Activate = 1 << 11, + ImGuiNavMoveFlags_DontSetNavHighlight = 1 << 12 // Do not alter the visible state of keyboard vs mouse nav highlight +}; + +enum ImGuiNavLayer +{ + ImGuiNavLayer_Main = 0, // Main scrolling layer + ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu) + ImGuiNavLayer_COUNT +}; + +struct ImGuiNavItemData +{ + ImGuiWindow* Window; // Init,Move // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window) + ImGuiID ID; // Init,Move // Best candidate item ID + ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID + ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space + ImGuiItemFlags InFlags; // ????,Move // Best candidate item flags + float DistBox; // Move // Best candidate box distance to current NavId + float DistCenter; // Move // Best candidate center distance to current NavId + float DistAxial; // Move // Best candidate axial distance to current NavId + + ImGuiNavItemData() { Clear(); } + void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; DistBox = DistCenter = DistAxial = FLT_MAX; } +}; + //----------------------------------------------------------------------------- // [SECTION] Columns support //----------------------------------------------------------------------------- @@ -1236,11 +1424,12 @@ struct ImGuiSettingsHandler }; //----------------------------------------------------------------------------- -// [SECTION] Metrics, Debug +// [SECTION] Metrics, Debug Tools //----------------------------------------------------------------------------- struct ImGuiMetricsConfig { + bool ShowStackTool; bool ShowWindowsRects; bool ShowWindowsBeginOrder; bool ShowTablesRects; @@ -1251,6 +1440,7 @@ struct ImGuiMetricsConfig ImGuiMetricsConfig() { + ShowStackTool = false; ShowWindowsRects = false; ShowWindowsBeginOrder = false; ShowTablesRects = false; @@ -1261,19 +1451,25 @@ struct ImGuiMetricsConfig } }; -struct IMGUI_API ImGuiStackSizes +struct ImGuiStackLevelInfo { - short SizeOfIDStack; - short SizeOfColorStack; - short SizeOfStyleVarStack; - short SizeOfFontStack; - short SizeOfFocusScopeStack; - short SizeOfGroupStack; - short SizeOfBeginPopupStack; + ImGuiID ID; + ImS8 QueryFrameCount; // >= 1: Query in progress + bool QuerySuccess; // Obtained result from DebugHookIdInfo() + char Desc[58]; // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?) - ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } - void SetToCurrentState(); - void CompareWithCurrentState(); + ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); } +}; + +// State for Stack tool queries +struct ImGuiStackTool +{ + int LastActiveFrame; + int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level + ImGuiID QueryId; // ID to query details for + ImVector Results; + + ImGuiStackTool() { memset(this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- @@ -1317,14 +1513,13 @@ struct ImGuiContext bool WithinEndChild; // Set within EndChild() bool GcCompactAll; // Request full GC bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log() - ImGuiID TestEngineHookIdInfo; // Will call test engine hooks: ImGuiTestEngineHook_IdInfo() from GetID() void* TestEngine; // Test engine user data // Windows state ImVector Windows; // Windows, sorted in display order, back to front ImVector WindowsFocusOrder; // Root windows, sorted in focus order, back to front. ImVector WindowsTempSortBuffer; // Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child - ImVector CurrentWindowStack; + ImVector CurrentWindowStack; ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow* int WindowsActiveCount; // Number of unique windows submitted by frame ImVec2 WindowsHoverPadding; // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING) @@ -1337,6 +1532,7 @@ struct ImGuiContext float WheelingWindowTimer; // Item/widgets state and tracking information + ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] ImGuiID HoveredId; // Hovered widget, filled during the frame ImGuiID HoveredIdPreviousFrame; bool HoveredIdAllowOverlap; @@ -1370,8 +1566,10 @@ struct ImGuiContext float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation. // Next window/item data - ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions + ImGuiItemFlags CurrentItemFlags; // == g.ItemFlagsStack.back() ImGuiNextItemData NextItemData; // Storage for SetNextItem** functions + ImGuiLastItemData LastItemData; // Storage for last submitted item (setup by ItemAdd) + ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions // Shared stacks ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin() @@ -1382,6 +1580,7 @@ struct ImGuiContext ImVectorGroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() ImVectorOpenPopupStack; // Which popups are open (persistent) ImVectorBeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) + int BeginMenuCount; // Viewports ImVector Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData. @@ -1393,37 +1592,44 @@ struct ImGuiContext ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0, also set when calling ActivateItem() ImGuiID NavActivateDownId; // ~~ IsNavInputDown(ImGuiNavInput_Activate) ? NavId : 0 ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0 - ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0 - ImGuiID NavJustTabbedId; // Just tabbed to this id. + ImGuiID NavActivateInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0; ImGuiActivateFlags_PreferInput will be set and NavActivateId will be 0. + ImGuiActivateFlags NavActivateFlags; ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest). ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest). ImGuiKeyModFlags NavJustMovedToKeyMods; ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. + ImGuiActivateFlags NavNextActivateFlags; ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. - ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. - int NavScoringCount; // Metrics for debugging ImGuiNavLayer NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. - int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRectRel is valid bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default) bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. - bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest + + // Navigation: Init & Move Requests + bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd() bool NavInitRequest; // Init request for appearing window to select first item bool NavInitRequestFromMove; ImGuiID NavInitResultId; // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called) ImRect NavInitResultRectRel; // Init request result rectangle (relative to parent window) - bool NavMoveRequest; // Move request for this frame - ImGuiNavMoveFlags NavMoveRequestFlags; - ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu) - ImGuiKeyModFlags NavMoveRequestKeyMods; - ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request + bool NavMoveSubmitted; // Move request submitted, will process result on next NewFrame() + bool NavMoveScoringItems; // Move request submitted, still scoring incoming items + bool NavMoveForwardToNextFrame; + ImGuiNavMoveFlags NavMoveFlags; + ImGuiScrollFlags NavMoveScrollFlags; + ImGuiKeyModFlags NavMoveKeyMods; + ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) + ImGuiDir NavMoveDirForDebug; ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? - ImGuiNavMoveResult NavMoveResultLocal; // Best move request candidate within NavWindow - ImGuiNavMoveResult NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) - ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) - ImGuiWindow* NavWrapRequestWindow; // Window which requested trying nav wrap-around. - ImGuiNavMoveFlags NavWrapRequestFlags; // Wrap-around operation flags. + ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. + ImRect NavScoringNoClipRect; // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted + int NavScoringDebugCount; // Metrics for debugging + int NavTabbingDir; // Generally -1 or +1, 0 when tabbing without a nav id + int NavTabbingCounter; // >0 when counting items for tabbing + ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow + ImGuiNavItemData NavMoveResultLocalVisible; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) + ImGuiNavItemData NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) + ImGuiNavItemData NavTabbingResultFirst; // First tabbing request candidate within NavWindow and flattened hierarchy // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize) ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most! @@ -1433,15 +1639,6 @@ struct ImGuiContext float NavWindowingHighlightAlpha; bool NavWindowingToggleLayer; - // Legacy Focus/Tabbing system (older than Nav, active even if Nav is disabled, misnamed. FIXME-NAV: This needs a redesign!) - ImGuiWindow* TabFocusRequestCurrWindow; // - ImGuiWindow* TabFocusRequestNextWindow; // - int TabFocusRequestCurrCounterRegular; // Any item being requested for focus, stored as an index (we on layout to be stable between the frame pressing TAB and the next frame, semi-ouch) - int TabFocusRequestCurrCounterTabStop; // Tab item being requested for focus, stored as an index - int TabFocusRequestNextCounterRegular; // Stored for next frame - int TabFocusRequestNextCounterTabStop; // " - bool TabFocusPressed; // - // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) ImGuiMouseCursor MouseCursor; @@ -1465,10 +1662,15 @@ struct ImGuiContext ImVector DragDropPayloadBufHeap; // We don't expose the ImVector<> directly, ImGuiPayload only holds pointer+size unsigned char DragDropPayloadBufLocal[16]; // Local buffer for small payloads + // Clipper + int ClipperTempDataStacked; + ImVector ClipperTempData; + // Table ImGuiTable* CurrentTable; - ImPool Tables; - ImVector CurrentTableStack; + int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size) + ImVector TablesTempData; // Temporary table data (buffers reused/shared across instances, support nesting) + ImPool Tables; // Persistent table data ImVector TablesLastTimeActive; // Last used timestamp of each tables (SOA, for efficient GC) ImVector DrawChannelsTempMergeBuffer; @@ -1479,22 +1681,25 @@ struct ImGuiContext ImVector ShrinkWidthBuffer; // Widget state - ImVec2 LastValidMousePos; + ImVec2 MouseLastValidPos; ImGuiInputTextState InputTextState; ImFont InputTextPasswordFont; ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc. ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets - float ColorEditLastHue; // Backup of last Hue associated to LastColor[3], so we can restore Hue in lossy RGB<>HSV round trips - float ColorEditLastSat; // Backup of last Saturation associated to LastColor[3], so we can restore Saturation in lossy RGB<>HSV round trips - float ColorEditLastColor[3]; + float ColorEditLastHue; // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips + float ColorEditLastSat; // Backup of last Saturation associated to LastColor, so we can restore Saturation in lossy RGB<>HSV round trips + ImU32 ColorEditLastColor; // RGB value with alpha set to 0. ImVec4 ColorPickerRef; // Initial/reference color at the time of opening the color picker. + ImGuiComboPreviewData ComboPreviewData; float SliderCurrentAccum; // Accumulated slider delta when using navigation controls. bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it? bool DragCurrentAccumDirty; float DragCurrentAccum; // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? - int TooltipOverrideCount; + float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled() + short DisabledStackSize; + short TooltipOverrideCount; float TooltipSlowDelay; // Time before slow tooltips appears (FIXME: This is temporary until we merge in tooltip timer+priority work) ImVector ClipboardHandlerData; // If no custom clipboard handler is defined ImVector MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once @@ -1529,12 +1734,14 @@ struct ImGuiContext // Debug Tools bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker()) - ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this id + ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID ImGuiMetricsConfig DebugMetricsConfig; + ImGuiStackTool DebugStackTool; // Misc float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds. int FramerateSecPerFrameIdx; + int FramerateSecPerFrameCount; float FramerateSecPerFrameAccum; int WantCaptureMouseNextFrame; // Explicit capture via CaptureKeyboardFromApp()/CaptureMouseFromApp() sets those flags int WantCaptureKeyboardNextFrame; @@ -1554,7 +1761,6 @@ struct ImGuiContext WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false; GcCompactAll = false; TestEngineHookItems = false; - TestEngineHookIdInfo = 0; TestEngine = NULL; WindowsActiveCount = 0; @@ -1565,6 +1771,7 @@ struct ImGuiContext WheelingWindow = NULL; WheelingWindowTimer = 0.0f; + DebugHookIdInfo = 0; HoveredId = HoveredIdPreviousFrame = 0; HoveredIdAllowOverlap = false; HoveredIdUsingMouseWheel = HoveredIdPreviousFrameUsingMouseWheel = false; @@ -1594,15 +1801,16 @@ struct ImGuiContext LastActiveId = 0; LastActiveIdTimer = 0.0f; + CurrentItemFlags = ImGuiItemFlags_None; + BeginMenuCount = 0; + NavWindow = NULL; - NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; - NavJustTabbedId = NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; + NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0; + NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; + NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; NavJustMovedToKeyMods = ImGuiKeyModFlags_None; NavInputSource = ImGuiInputSource_None; - NavScoringRect = ImRect(); - NavScoringCount = 0; NavLayer = ImGuiNavLayer_Main; - NavIdTabCounter = INT_MAX; NavIdIsAlive = false; NavMousePosDirty = false; NavDisableHighlight = true; @@ -1611,23 +1819,21 @@ struct ImGuiContext NavInitRequest = false; NavInitRequestFromMove = false; NavInitResultId = 0; - NavMoveRequest = false; - NavMoveRequestFlags = ImGuiNavMoveFlags_None; - NavMoveRequestForward = ImGuiNavForward_None; - NavMoveRequestKeyMods = ImGuiKeyModFlags_None; - NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None; - NavWrapRequestWindow = NULL; - NavWrapRequestFlags = ImGuiNavMoveFlags_None; + NavMoveSubmitted = false; + NavMoveScoringItems = false; + NavMoveForwardToNextFrame = false; + NavMoveFlags = ImGuiNavMoveFlags_None; + NavMoveScrollFlags = ImGuiScrollFlags_None; + NavMoveKeyMods = ImGuiKeyModFlags_None; + NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; + NavScoringDebugCount = 0; + NavTabbingDir = 0; + NavTabbingCounter = 0; NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; - TabFocusRequestCurrWindow = TabFocusRequestNextWindow = NULL; - TabFocusRequestCurrCounterRegular = TabFocusRequestCurrCounterTabStop = INT_MAX; - TabFocusRequestNextCounterRegular = TabFocusRequestNextCounterTabStop = INT_MAX; - TabFocusPressed = false; - DimBgRatio = 0.0f; MouseCursor = ImGuiMouseCursor_Arrow; @@ -1643,19 +1849,23 @@ struct ImGuiContext DragDropHoldJustPressedId = 0; memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); + ClipperTempDataStacked = 0; + CurrentTable = NULL; + TablesTempDataStacked = 0; CurrentTabBar = NULL; - LastValidMousePos = ImVec2(0.0f, 0.0f); TempInputId = 0; - ColorEditOptions = ImGuiColorEditFlags__OptionsDefault; + ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; ColorEditLastHue = ColorEditLastSat = 0.0f; - ColorEditLastColor[0] = ColorEditLastColor[1] = ColorEditLastColor[2] = FLT_MAX; + ColorEditLastColor = 0; SliderCurrentAccum = 0.0f; SliderCurrentAccumDirty = false; DragCurrentAccumDirty = false; DragCurrentAccum = 0.0f; DragSpeedDefaultRatio = 1.0f / 100.0f; + DisabledAlphaBackup = 0.0f; + DisabledStackSize = 0; ScrollbarClickDeltaToGrabCenter = 0.0f; TooltipOverrideCount = 0; TooltipSlowDelay = 0.50f; @@ -1680,7 +1890,7 @@ struct ImGuiContext DebugItemPickerBreakId = 0; memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); - FramerateSecPerFrameIdx = 0; + FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0; FramerateSecPerFrameAccum = 0.0f; WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1; memset(TempBuffer, 0, sizeof(TempBuffer)); @@ -1709,17 +1919,12 @@ struct IMGUI_API ImGuiWindowTempData ImVec1 Indent; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) ImVec1 ColumnsOffset; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. ImVec1 GroupOffset; - - // Last item status - ImGuiID LastItemId; // ID for last item - ImGuiItemStatusFlags LastItemStatusFlags; // Status flags for last item (see ImGuiItemStatusFlags_) - ImRect LastItemRect; // Interaction rect for last item - ImRect LastItemDisplayRect; // End-user display rect for last item (only valid if LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) + ImVec2 CursorStartPosLossyness;// Record the loss of precision of CursorStartPos due to really large scrolling amount. This is used by clipper to compensentate and fix the most common use case of large scroll area. // Keyboard/Gamepad navigation ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) - int NavLayerActiveMask; // Which layers have been written to (result from previous frame) - int NavLayerActiveMaskNext; // Which layers have been written to (accumulator for current frame) + short NavLayersActiveMask; // Which layers have been written to (result from previous frame) + short NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame) ImGuiID NavFocusScopeIdCurrent; // Current focus scope ID while appending bool NavHideHighlightOneFrame; bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) @@ -1736,17 +1941,13 @@ struct IMGUI_API ImGuiWindowTempData int CurrentTableIdx; // Current table index (into g.Tables) ImGuiLayoutType LayoutType; ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() - int FocusCounterRegular; // (Legacy Focus/Tabbing system) Sequential counter, start at -1 and increase as assigned via FocusableItemRegister() (FIXME-NAV: Needs redesign) - int FocusCounterTabStop; // (Legacy Focus/Tabbing system) Same, but only count widgets which you can Tab through. // Local parameters stacks // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. - ImGuiItemFlags ItemFlags; // == g.ItemFlagsStack.back() float ItemWidth; // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window). float TextWrapPos; // Current text wrap pos. ImVector ItemWidthStack; // Store item widths to restore (attention: .back() is not == ItemWidth) ImVector TextWrapPosStack; // Store text wrap pos to restore (attention: .back() is not == TextWrapPos) - ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting }; // Storage for one window @@ -1783,6 +1984,7 @@ struct IMGUI_API ImGuiWindow bool Appearing; // Set during the frame where the window is appearing (or re-appearing) bool Hidden; // Do not display (== HiddenFrames*** > 0) bool IsFallbackWindow; // Set on the "Debug##Default" window. + bool IsExplicitChild; // Set when passed _ChildWindow, left to false by BeginDocked() bool HasCloseButton; // Set when the window has a close button (p_open != NULL) signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) @@ -1829,8 +2031,10 @@ struct IMGUI_API ImGuiWindow ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) ImDrawList DrawListInst; - ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. - ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window == Top-level window. + ImGuiWindow* ParentWindow; // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL. + ImGuiWindow* ParentWindowInBeginStack; + ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes. + ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. @@ -1863,19 +2067,6 @@ public: ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } }; -// Backup and restore just enough data to be able to use IsItemHovered() on item A after another B in the same window has overwritten the data. -struct ImGuiLastItemDataBackup -{ - ImGuiID LastItemId; - ImGuiItemStatusFlags LastItemStatusFlags; - ImRect LastItemRect; - ImRect LastItemDisplayRect; - - ImGuiLastItemDataBackup() { Backup(); } - void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemStatusFlags = window->DC.LastItemStatusFlags; LastItemRect = window->DC.LastItemRect; LastItemDisplayRect = window->DC.LastItemDisplayRect; } - void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; } -}; - //----------------------------------------------------------------------------- // [SECTION] Tab bar, Tab item support //----------------------------------------------------------------------------- @@ -1896,7 +2087,7 @@ enum ImGuiTabItemFlagsPrivate_ ImGuiTabItemFlags_Button = 1 << 21 // Used by TabItemButton, change the tab item behavior to mimic a button }; -// Storage for one active tab item (sizeof() 28~32 bytes) +// Storage for one active tab item (sizeof() 40 bytes) struct ImGuiTabItem { ImGuiID ID; @@ -1906,16 +2097,16 @@ struct ImGuiTabItem float Offset; // Position relative to beginning of tab float Width; // Width currently displayed float ContentWidth; // Width of label, stored during BeginTabItem() call - ImS16 NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames + ImS32 NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames ImS16 BeginOrder; // BeginTabItem() order, used to re-order tabs after toggling ImGuiTabBarFlags_Reorderable ImS16 IndexDuringLayout; // Index only used during TabBarLayout() bool WantClose; // Marked as closed by SetTabItemClosed() - ImGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; NameOffset = BeginOrder = IndexDuringLayout = -1; } + ImGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; } }; // Storage for a tab bar (sizeof() 152 bytes) -struct ImGuiTabBar +struct IMGUI_API ImGuiTabBar { ImVector Tabs; ImGuiTabBarFlags Flags; @@ -1953,7 +2144,7 @@ struct ImGuiTabBar int GetTabOrder(const ImGuiTabItem* tab) const { return Tabs.index_from_ptr(tab); } const char* GetTabName(const ImGuiTabItem* tab) const { - IM_ASSERT(tab->NameOffset != -1 && (int)tab->NameOffset < TabsNames.Buf.Size); + IM_ASSERT(tab->NameOffset != -1 && tab->NameOffset < TabsNames.Buf.Size); return TabsNames.Buf.Data + tab->NameOffset; } }; @@ -1962,8 +2153,6 @@ struct ImGuiTabBar // [SECTION] Table support //----------------------------------------------------------------------------- -#ifdef IMGUI_HAS_TABLE - #define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color. #define IMGUI_TABLE_MAX_COLUMNS 64 // sizeof(ImU64) * 8. This is solely because we frequently encode columns set in a ImU64. #define IMGUI_TABLE_MAX_DRAW_CHANNELS (4 + 64 * 2) // See TableSetupDrawChannels() @@ -2002,10 +2191,11 @@ struct ImGuiTableColumn ImGuiTableColumnIdx NextEnabledColumn; // Index of next enabled/visible column within Columns[], -1 if last enabled/visible column ImGuiTableColumnIdx SortOrder; // Index of this column within sort specs, -1 if not sorting on this column, 0 for single-sort, may be >0 on multi-sort ImGuiTableDrawChannelIdx DrawChannelCurrent; // Index within DrawSplitter.Channels[] - ImGuiTableDrawChannelIdx DrawChannelFrozen; - ImGuiTableDrawChannelIdx DrawChannelUnfrozen; - bool IsEnabled; // Is the column not marked Hidden by the user? (even if off view, e.g. clipped by scrolling). - bool IsEnabledNextFrame; + ImGuiTableDrawChannelIdx DrawChannelFrozen; // Draw channels for frozen rows (often headers) + ImGuiTableDrawChannelIdx DrawChannelUnfrozen; // Draw channels for unfrozen rows + bool IsEnabled; // IsUserEnabled && (Flags & ImGuiTableColumnFlags_Disabled) == 0 + bool IsUserEnabled; // Is the column not marked Hidden by the user? (unrelated to being off view, e.g. clipped by scrolling). + bool IsUserEnabledNextFrame; bool IsVisibleX; // Is actually in view (e.g. overlapping the host window clipping rectangle, not scrolled). bool IsVisibleY; bool IsRequestOutput; // Return value for TableSetColumnIndex() / TableNextColumn(): whether we request user to output contents or not. @@ -2040,12 +2230,13 @@ struct ImGuiTableCellData ImGuiTableColumnIdx Column; // Column number }; -// FIXME-TABLE: transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData -struct ImGuiTable +// FIXME-TABLE: more transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData +struct IMGUI_API ImGuiTable { ImGuiID ID; ImGuiTableFlags Flags; void* RawData; // Single allocation to hold Columns[], DisplayOrderToIndex[] and RowCellData[] + ImGuiTableTempData* TempData; // Transient data while table is active. Point within g.CurrentTableStack[] ImSpan Columns; // Point within RawData[] ImSpan DisplayOrderToIndex; // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1) ImSpan RowCellData; // Point within RawData[]. Store cells background requests for current row. @@ -2097,22 +2288,13 @@ struct ImGuiTable ImRect Bg0ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped ImRect Bg2ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect. ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window. - ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable() - ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() ImRect HostBackupInnerClipRect; // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground() - ImVec2 HostBackupPrevLineSize; // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable() - ImVec2 HostBackupCurrLineSize; // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable() - ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable() - ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() - ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable() - float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() - int HostBackupItemWidthStackSize;// Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable() ImGuiWindow* OuterWindow; // Parent window for the table ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window) ImGuiTextBuffer ColumnsNames; // Contiguous buffer holding columns names - ImDrawListSplitter DrawSplitter; // We carry our own ImDrawList splitter to allow recursion (FIXME: could be stored outside, worst case we need 1 splitter per recursing table) + ImDrawListSplitter* DrawSplitter; // Shortcut to TempData->DrawSplitter while in table. Isolate draw commands per columns to avoid switching clip rect constantly ImGuiTableColumnSortSpecs SortSpecsSingle; - ImVector SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would work be good. + ImVector SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would be good. ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs() ImGuiTableColumnIdx SortSpecsCount; ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount) @@ -2155,8 +2337,31 @@ struct ImGuiTable bool MemoryCompacted; bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis - IMGUI_API ImGuiTable() { memset(this, 0, sizeof(*this)); LastFrameActive = -1; } - IMGUI_API ~ImGuiTable() { IM_FREE(RawData); } + ImGuiTable() { memset(this, 0, sizeof(*this)); LastFrameActive = -1; } + ~ImGuiTable() { IM_FREE(RawData); } +}; + +// Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table). +// - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure. +// - We also leave out of this structure data that tend to be particularly useful for debugging/metrics. +struct IMGUI_API ImGuiTableTempData +{ + int TableIndex; // Index in g.Tables.Buf[] pool + float LastTimeActive; // Last timestamp this structure was used + + ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() + ImDrawListSplitter DrawSplitter; + + ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable() + ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() + ImVec2 HostBackupPrevLineSize; // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable() + ImVec2 HostBackupCurrLineSize; // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable() + ImVec2 HostBackupCursorMaxPos; // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable() + ImVec1 HostBackupColumnsOffset; // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable() + float HostBackupItemWidth; // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable() + int HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable() + + ImGuiTableTempData() { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; } }; // sizeof() ~ 12 @@ -2197,8 +2402,6 @@ struct ImGuiTableSettings ImGuiTableColumnSettings* GetColumnSettings() { return (ImGuiTableColumnSettings*)(this + 1); } }; -#endif // #ifdef IMGUI_HAS_TABLE - //----------------------------------------------------------------------------- // [SECTION] ImGui internal API // No guarantee of forward compatibility here! @@ -2217,14 +2420,16 @@ namespace ImGui IMGUI_API ImGuiWindow* FindWindowByName(const char* name); IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); - IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); + IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy); + IMGUI_API bool IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); - IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow* window); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0); IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0); IMGUI_API void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size); + inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } + inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } // Windows: Display Order and Focus Order IMGUI_API void FocusWindow(ImGuiWindow* window); @@ -2232,6 +2437,9 @@ namespace ImGui IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); + IMGUI_API void BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* above_window); + IMGUI_API int FindWindowDisplayIndex(ImGuiWindow* window); + IMGUI_API ImGuiWindow* FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window); // Fonts, drawing IMGUI_API void SetCurrentFont(ImFont* font); @@ -2270,14 +2478,21 @@ namespace ImGui IMGUI_API void SetScrollY(ImGuiWindow* window, float scroll_y); IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio); IMGUI_API void SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio); - IMGUI_API ImVec2 ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect); + + // Early work-in-progress API (ScrollToItem() will become public) + IMGUI_API void ScrollToItem(ImGuiScrollFlags flags = 0); + IMGUI_API void ScrollToRect(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0); + IMGUI_API ImVec2 ScrollToRectEx(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0); +//#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + inline void ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& rect) { ScrollToRect(window, rect, ImGuiScrollFlags_KeepVisibleEdgeY); } +//#endif // Basic Accessors - inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemId; } // Get ID of last item (~~ often same ImGui::GetID(label) beforehand) - inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemStatusFlags; } + inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.LastItemData.ID; } // Get ID of last item (~~ often same ImGui::GetID(label) beforehand) + inline ImGuiItemStatusFlags GetItemStatusFlags(){ ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; } + inline ImGuiItemFlags GetItemFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.InFlags; } inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; } inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; } - inline ImGuiItemFlags GetItemsFlags() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.ItemFlags; } IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window); IMGUI_API void ClearActiveID(); @@ -2291,21 +2506,32 @@ namespace ImGui // Basic Helpers for widget code IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f); IMGUI_API void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f); - IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL); + IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0); IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); - IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged); - IMGUI_API void SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); - IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id); // Return true if focus is requested - IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); + IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); + IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void PushMultiItemsWidths(int components, float width_full); - IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); - IMGUI_API void PopItemFlag(); IMGUI_API bool IsItemToggledSelection(); // Was the last item selection toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly) IMGUI_API ImVec2 GetContentRegionMaxAbs(); IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); + // Parameter stacks + IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); + IMGUI_API void PopItemFlag(); + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + // Currently refactoring focus/nav/tabbing system + // If you have old/custom copy-and-pasted widgets that used FocusableItemRegister(): + // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)' + // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' + // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_FocusedTabbing) != 0 || g.NavActivateInputId == id' (WIP) + // Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText() + inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd() + inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem +#endif + // Logging/Capture IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name. IMGUI_API void LogToBuffer(int auto_open_depth = -1); // Start logging/capturing to internal buffer @@ -2317,25 +2543,41 @@ namespace ImGui IMGUI_API void OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None); IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup); IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup); + IMGUI_API void ClosePopupsExceptModals(); IMGUI_API bool IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags); IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); - IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags); + IMGUI_API void BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags); + IMGUI_API ImRect GetPopupAllowedExtentRect(ImGuiWindow* window); IMGUI_API ImGuiWindow* GetTopMostPopupModal(); + IMGUI_API ImGuiWindow* GetTopMostAndVisiblePopupModal(); IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); + + // Menus IMGUI_API bool BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags); + IMGUI_API bool BeginMenuEx(const char* label, const char* icon, bool enabled = true); + IMGUI_API bool MenuItemEx(const char* label, const char* icon, const char* shortcut = NULL, bool selected = false, bool enabled = true); + + // Combos + IMGUI_API bool BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags); + IMGUI_API bool BeginComboPreview(); + IMGUI_API void EndComboPreview(); // Gamepad/Keyboard Navigation IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); + IMGUI_API void NavInitRequestApplyResult(); IMGUI_API bool NavMoveRequestButNoResultYet(); + IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); + IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); + IMGUI_API void NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result); IMGUI_API void NavMoveRequestCancel(); - IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags); + IMGUI_API void NavMoveRequestApplyResult(); IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode); IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f); IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate); IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. - IMGUI_API void SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); + IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); // Focus Scope (WIP) // This is generally used to identify a selection set (multiple of which may be in the same window), as selection @@ -2348,6 +2590,7 @@ namespace ImGui // Inputs // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions. IMGUI_API void SetItemUsingMouseWheel(); + IMGUI_API void SetActiveIdUsingNavAndKeys(); inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; } inline bool IsActiveIdUsingNavInput(ImGuiNavInput input) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavInputMask & (1 << input)) != 0; } inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; IM_ASSERT(key < 64); return (g.ActiveIdUsingKeyInputMask & ((ImU64)1 << key)) != 0; } @@ -2413,6 +2656,7 @@ namespace ImGui IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); IMGUI_API void TableRemove(ImGuiTable* table); IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table); + IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table); IMGUI_API void TableGcCompactSettings(); // Tables: Settings @@ -2473,7 +2717,7 @@ namespace ImGui IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); IMGUI_API void Scrollbar(ImGuiAxis axis); - IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* p_scroll_v, float avail_v, float contents_v, ImDrawFlags flags); + IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags flags); IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col); IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis); IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis); @@ -2536,12 +2780,16 @@ namespace ImGui // Debug Tools IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); - inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(window->DC.LastItemRect.Min, window->DC.LastItemRect.Max, col); } + IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); + inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col); } inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; } + IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); + IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end); IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns); IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label); IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb); + IMGUI_API void DebugNodeFont(ImFont* font); IMGUI_API void DebugNodeStorage(ImGuiStorage* storage, const char* label); IMGUI_API void DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label); IMGUI_API void DebugNodeTable(ImGuiTable* table); @@ -2549,6 +2797,7 @@ namespace ImGui IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label); IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings); IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); + IMGUI_API void DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack); IMGUI_API void DebugNodeViewport(ImGuiViewportP* viewport); IMGUI_API void DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb); @@ -2583,20 +2832,14 @@ IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table #ifdef IMGUI_ENABLE_TEST_ENGINE extern void ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, const ImRect& bb, ImGuiID id); extern void ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags); -extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id); -extern void ImGuiTestEngineHook_IdInfo(ImGuiContext* ctx, ImGuiDataType data_type, ImGuiID id, const void* data_id, const void* data_id_end); extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...); +extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id); + #define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _BB, _ID) // Register item bounding box #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) #define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log -#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) if (g.TestEngineHookIdInfo == id) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA)); -#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) if (g.TestEngineHookIdInfo == id) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA), (const void*)(_DATA2)); #else -#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) do { } while (0) -#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) do { } while (0) -#define IMGUI_TEST_ENGINE_LOG(_FMT,...) do { } while (0) -#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) do { } while (0) -#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) do { } while (0) +#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)0) #endif //----------------------------------------------------------------------------- diff --git a/r5dev/include/imgui_stdlib.h b/r5dev/thirdparty/imgui/include/imgui_stdlib.h similarity index 100% rename from r5dev/include/imgui_stdlib.h rename to r5dev/thirdparty/imgui/include/imgui_stdlib.h diff --git a/r5dev/thirdparty/imgui/include/imgui_utility.h b/r5dev/thirdparty/imgui/include/imgui_utility.h new file mode 100644 index 00000000..d221c4c9 --- /dev/null +++ b/r5dev/thirdparty/imgui/include/imgui_utility.h @@ -0,0 +1,31 @@ +#pragma once + +///////////////////////////////////////////////////////////////////////////// +// Internals +int Stricmp(const char* s1, const char* s2); +int Strnicmp(const char* s1, const char* s2, int n); +char* Strdup(const char* s); +void Strtrim(char* s); + +class ImGuiConfig +{ +public: + struct + { + int m_nBind0 = VK_OEM_3; + int m_nBind1 = VK_INSERT; + int m_nAutoClearLimit = 300; + bool m_bAutoClear = true; + } IConsole_Config; + + struct + { + int m_nBind0 = VK_HOME; + int m_nBind1 = VK_F10; + } IBrowser_Config; + + void Load(); + void Save(); +}; + +extern ImGuiConfig* g_pImGuiConfig; diff --git a/external/imgui/include/imstb_rectpack.h b/r5dev/thirdparty/imgui/include/imstb_rectpack.h similarity index 98% rename from external/imgui/include/imstb_rectpack.h rename to r5dev/thirdparty/imgui/include/imstb_rectpack.h index ff2a85df..39589521 100644 --- a/external/imgui/include/imstb_rectpack.h +++ b/r5dev/thirdparty/imgui/include/imstb_rectpack.h @@ -34,7 +34,7 @@ // Minor features // Martins Mozeiko // github:IntellectualKitty -// +// // Bugfixes / warning fixes // Jeremy Jaussaud // Fabian Giesen @@ -441,7 +441,7 @@ static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int widt } } tail = tail->next; - } + } } fr.prev_link = best; @@ -602,38 +602,38 @@ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ diff --git a/external/imgui/include/imstb_textedit.h b/r5dev/thirdparty/imgui/include/imstb_textedit.h similarity index 98% rename from external/imgui/include/imstb_textedit.h rename to r5dev/thirdparty/imgui/include/imstb_textedit.h index 76446709..2c635b27 100644 --- a/external/imgui/include/imstb_textedit.h +++ b/r5dev/thirdparty/imgui/include/imstb_textedit.h @@ -1,5 +1,5 @@ // [DEAR IMGUI] -// This is a slightly modified version of stb_textedit.h 1.13. +// This is a slightly modified version of stb_textedit.h 1.13. // Those changes would need to be pushed into nothings/stb: // - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) // Grep for [DEAR IMGUI] to find the changes. @@ -19,7 +19,7 @@ // texts, as its performance does not scale and it has limited undo). // // Non-trivial behaviors are modelled after Windows text controls. -// +// // // LICENSE // @@ -217,20 +217,20 @@ // call this with the mouse x,y on a mouse down; it will update the cursor // and reset the selection start/end to the cursor point. the x,y must // be relative to the text widget, with (0,0) being the top left. -// +// // drag: // call this with the mouse x,y on a mouse drag/up; it will update the // cursor and the selection end point -// +// // cut: // call this to delete the current selection; returns true if there was // one. you should FIRST copy the current selection to the system paste buffer. // (To copy, just copy the current selection out of the string yourself.) -// +// // paste: // call this to paste text at the current cursor point or over the current // selection if there is one. -// +// // key: // call this for keyboard inputs sent to the textfield. you can use it // for "key down" events or for "translated" key events. if you need to @@ -241,7 +241,7 @@ // clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to // anything other type you wante before including. // -// +// // When rendering, you can read the cursor position and selection state from // the STB_TexteditState. // @@ -716,9 +716,11 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta state->has_preferred_x = 0; return 1; } - // remove the undo since we didn't actually insert the characters - if (state->undostate.undo_point) - --state->undostate.undo_point; + // [DEAR IMGUI] + //// remove the undo since we didn't actually insert the characters + //if (state->undostate.undo_point) + // --state->undostate.undo_point; + // note: paste failure will leave deleted selection, may be restored with an undo (see https://github.com/nothings/stb/issues/734 for details) return 0; } @@ -764,7 +766,7 @@ retry: state->insert_mode = !state->insert_mode; break; #endif - + case STB_TEXTEDIT_K_UNDO: stb_text_undo(str, state); state->has_preferred_x = 0; @@ -779,7 +781,7 @@ retry: // if currently there's a selection, move cursor to start of selection if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_move_to_first(state); - else + else if (state->cursor > 0) --state->cursor; state->has_preferred_x = 0; @@ -828,7 +830,7 @@ retry: #ifdef STB_TEXTEDIT_MOVEWORDRIGHT case STB_TEXTEDIT_K_WORDRIGHT: - if (STB_TEXT_HAS_SELECTION(state)) + if (STB_TEXT_HAS_SELECTION(state)) stb_textedit_move_to_last(str, state); else { state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); @@ -922,7 +924,7 @@ retry: } break; } - + case STB_TEXTEDIT_K_UP: case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: case STB_TEXTEDIT_K_PGUP: @@ -1014,7 +1016,7 @@ retry: } state->has_preferred_x = 0; break; - + #ifdef STB_TEXTEDIT_K_TEXTSTART2 case STB_TEXTEDIT_K_TEXTSTART2: #endif @@ -1031,7 +1033,7 @@ retry: state->select_start = state->select_end = 0; state->has_preferred_x = 0; break; - + #ifdef STB_TEXTEDIT_K_TEXTSTART2 case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: #endif @@ -1410,38 +1412,38 @@ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ diff --git a/external/imgui/include/imstb_truetype.h b/r5dev/thirdparty/imgui/include/imstb_truetype.h similarity index 99% rename from external/imgui/include/imstb_truetype.h rename to r5dev/thirdparty/imgui/include/imstb_truetype.h index fc815d74..48c20261 100644 --- a/external/imgui/include/imstb_truetype.h +++ b/r5dev/thirdparty/imgui/include/imstb_truetype.h @@ -51,7 +51,7 @@ // Rob Loach Cort Stratton // Kenney Phillis Jr. github:oyvindjam // Brian Costabile github:vassvik -// +// // VERSION HISTORY // // 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() @@ -212,7 +212,7 @@ // // Advancing for the next character: // Call GlyphHMetrics, and compute 'current_point += SF * advance'. -// +// // // ADVANCED USAGE // @@ -257,7 +257,7 @@ // Curve tessellation 120 LOC \__ 550 LOC Bitmap creation // Bitmap management 100 LOC / // Baked bitmap interface 70 LOC / -// Font name matching & access 150 LOC ---- 150 +// Font name matching & access 150 LOC ---- 150 // C runtime library abstraction 60 LOC ---- 60 // // @@ -350,7 +350,7 @@ int main(int argc, char **argv) } return 0; } -#endif +#endif // // Output: // @@ -364,9 +364,9 @@ int main(int argc, char **argv) // :@@. M@M // @@@o@@@@ // :M@@V:@@. -// +// ////////////////////////////////////////////////////////////////////////////// -// +// // Complete program: print "Hello World!" banner, with bugs // #if 0 @@ -667,7 +667,7 @@ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, cons // Calling these functions in sequence is roughly equivalent to calling // stbtt_PackFontRanges(). If you more control over the packing of multiple // fonts, or if you want to pack custom data into a font texture, take a look -// at the source to of stbtt_PackFontRanges() and create a custom version +// at the source to of stbtt_PackFontRanges() and create a custom version // using these functions, e.g. call GatherRects multiple times, // building up a single array of rects, then call PackRects once, // then call RenderIntoRects repeatedly. This may result in a @@ -975,7 +975,7 @@ STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, floa // and computing from that can allow drop-out prevention). // // The algorithm has not been optimized at all, so expect it to be slow -// if computing lots of characters or very large sizes. +// if computing lots of characters or very large sizes. @@ -1732,7 +1732,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s if (i != 0) num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - // now start the new one + // now start the new one start_off = !(flags & 1); if (start_off) { // if we start off with an off-curve point, then when we need to find a point on the curve @@ -1785,7 +1785,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s int comp_num_verts = 0, i; stbtt_vertex *comp_verts = 0, *tmp = 0; float mtx[6] = {1,0,0,1,0,0}, m, n; - + flags = ttSHORT(comp); comp+=2; gidx = ttSHORT(comp); comp+=2; @@ -1815,7 +1815,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; } - + // Find transformation scales. m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); @@ -2746,7 +2746,7 @@ static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, i float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); STBTT_assert(z != NULL); if (!z) return z; - + // round dx down to avoid overshooting if (dxdy < 0) z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); @@ -2824,7 +2824,7 @@ static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__ac } } } - + e = e->next; } } @@ -3554,7 +3554,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info { int ix0,iy0,ix1,iy1; stbtt__bitmap gbm; - stbtt_vertex *vertices; + stbtt_vertex *vertices; int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); if (scale_x == 0) scale_x = scale_y; @@ -3577,7 +3577,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info if (height) *height = gbm.h; if (xoff ) *xoff = ix0; if (yoff ) *yoff = iy0; - + if (gbm.w && gbm.h) { gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); if (gbm.pixels) { @@ -3588,7 +3588,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info } STBTT_free(vertices, info->userdata); return gbm.pixels; -} +} STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) { @@ -3600,7 +3600,7 @@ STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigne int ix0,iy0; stbtt_vertex *vertices; int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); - stbtt__bitmap gbm; + stbtt__bitmap gbm; stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); gbm.pixels = output; @@ -3622,7 +3622,7 @@ STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char * STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); -} +} STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) { @@ -3637,7 +3637,7 @@ STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, uns STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); -} +} STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) { @@ -3762,7 +3762,7 @@ static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *no con->y = 0; con->bottom_y = 0; STBTT__NOTUSED(nodes); - STBTT__NOTUSED(num_nodes); + STBTT__NOTUSED(num_nodes); } static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) @@ -4147,7 +4147,7 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char n = 0; for (i=0; i < num_ranges; ++i) n += ranges[i].num_chars; - + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); if (rects == NULL) return 0; @@ -4158,7 +4158,7 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); stbtt_PackFontRangesPackRects(spc, rects, n); - + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); STBTT_free(rects, spc->user_allocator_context); @@ -4319,7 +4319,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; - if (x_inter < x) + if (x_inter < x) winding += (y0 < y1) ? 1 : -1; } } @@ -4345,7 +4345,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex y1 = (int)verts[i ].y; if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; - if (x_inter < x) + if (x_inter < x) winding += (y0 < y1) ? 1 : -1; } } else { @@ -4357,7 +4357,7 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex if (hits[1][0] < 0) winding += (hits[1][1] < 0 ? -1 : 1); } - } + } } } return winding; @@ -4438,7 +4438,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc // invert for y-downwards bitmaps scale_y = -scale_y; - + { int x,y,i,j; float *precompute; @@ -4587,7 +4587,7 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc STBTT_free(verts, info->userdata); } return data; -} +} STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) { @@ -4605,7 +4605,7 @@ STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) // // check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string -static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) { stbtt_int32 i=0; @@ -4644,7 +4644,7 @@ static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, s return i; } -static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) { return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); } @@ -4773,7 +4773,7 @@ STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) { - return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); } STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) @@ -4866,38 +4866,38 @@ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ diff --git a/external/imgui/src/imgui.cpp b/r5dev/thirdparty/imgui/src/imgui.cpp similarity index 81% rename from external/imgui/src/imgui.cpp rename to r5dev/thirdparty/imgui/src/imgui.cpp index a2557f84..41fef144 100644 --- a/external/imgui/src/imgui.cpp +++ b/r5dev/thirdparty/imgui/src/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.83 WIP +// dear imgui, v1.86 // (main code and documentation) // Help: @@ -11,11 +11,14 @@ // - FAQ http://dearimgui.org/faq // - Homepage & latest https://github.com/ocornut/imgui // - Releases & changelog https://github.com/ocornut/imgui/releases -// - Gallery https://github.com/ocornut/imgui/issues/3793 (please post your screenshots/video there!) +// - Gallery https://github.com/ocornut/imgui/issues/4451 (please post your screenshots/video there!) // - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Glossary https://github.com/ocornut/imgui/wiki/Glossary // - Issues & support https://github.com/ocornut/imgui/issues -// - Discussions https://github.com/ocornut/imgui/discussions + +// Getting Started? +// - For first-time users having issues compiling/linking/running or issues loading fonts: +// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. // See LICENSE.txt for copyright and licensing details (standard MIT License). @@ -79,6 +82,7 @@ CODE // [SECTION] VIEWPORTS // [SECTION] PLATFORM DEPENDENT HELPERS // [SECTION] METRICS/DEBUGGER WINDOW +// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) */ @@ -304,9 +308,9 @@ CODE } else { - // The texture for the draw call is specified by pcmd->TextureId. + // The texture for the draw call is specified by pcmd->GetTexID(). // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization. - MyEngineBindTexture((MyTexture*)pcmd->TextureId); + MyEngineBindTexture((MyTexture*)pcmd->GetTexID()); // We are using scissoring to clip some objects. All low-level graphics API should support it. // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches @@ -376,6 +380,15 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2021/12/20 (1.86) - backends: removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings + - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function. + - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful. + - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019): + - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList() + - ImFont::GlyphRangesBuilder -> use ImFontGlyphRangesBuilder + - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID(). + - if you are using official backends from the source tree: you have nothing to do. + - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID(). - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags. - ImDrawCornerFlags_TopLeft -> use ImDrawFlags_RoundCornersTopLeft - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight @@ -393,7 +406,7 @@ CODE - ImGui::SetScrollHere() -> use ImGui::SetScrollHereY() - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing. - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() "bool closed" parameter to "ImDrawFlags flags". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future. - - 2021/02/22 (1.82) - win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'. + - 2021/02/22 (1.82) - (*undone in 1.84*) win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'. - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed. - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete). - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete). @@ -712,9 +725,11 @@ CODE Q&A: Usage ---------- - Q: Why is my widget not reacting when I click on it? - Q: How can I have widgets with an empty label? - Q: How can I have multiple widgets with the same label? + Q: About the ID Stack system.. + - Why is my widget not reacting when I click on it? + - How can I have widgets with an empty label? + - How can I have multiple widgets with the same label? + - How can I have multiple windows with the same label? Q: How can I display an image? What is ImTextureID, how does it works? Q: How can I use my own math types instead of ImVec2/ImVec4? Q: How can I interact with standard C++ types (such as std::string and std::vector)? @@ -765,13 +780,13 @@ CODE #define _CRT_SECURE_NO_WARNINGS #endif -#include "imgui.h" +#include "thirdparty/imgui/include/imgui.h" #ifndef IMGUI_DISABLE #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif -#include "imgui_internal.h" +#include "thirdparty/imgui/include/imgui_internal.h" // System includes #include // toupper @@ -782,6 +797,11 @@ CODE #include // intptr_t #endif +// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled +#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) +#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS +#endif + // [Windows] OS specific includes (optional) #if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) #define IMGUI_DISABLE_WIN32_FUNCTIONS @@ -816,6 +836,9 @@ CODE #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything @@ -892,34 +915,41 @@ namespace ImGui static void NavUpdate(); static void NavUpdateWindowing(); static void NavUpdateWindowingOverlay(); -static void NavUpdateMoveResult(); -static void NavUpdateInitResult(); +static void NavUpdateCancelRequest(); +static void NavUpdateCreateMoveRequest(); +static void NavUpdateCreateTabbingRequest(); static float NavUpdatePageUpPageDown(); static inline void NavUpdateAnyRequestFlag(); +static void NavUpdateCreateWrappingRequest(); static void NavEndFrame(); -static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand); -static void NavApplyItemToResult(ImGuiNavMoveResult* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel); -static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, ImGuiID id); +static bool NavScoreItem(ImGuiNavItemData* result); +static void NavApplyItemToResult(ImGuiNavItemData* result); +static void NavProcessItem(); +static void NavProcessItemForTabbingRequest(ImGuiID id); static ImVec2 NavCalcPreferredRefPos(); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); static void NavRestoreLayer(ImGuiNavLayer layer); +static void NavRestoreHighlightAfterMove(); static int FindWindowFocusIndex(ImGuiWindow* window); -// Error Checking +// Error Checking and Debug Tools static void ErrorCheckNewFrameSanityChecks(); static void ErrorCheckEndFrameSanityChecks(); +static void UpdateDebugToolItemPicker(); +static void UpdateDebugToolStackQueries(); // Misc static void UpdateSettings(); static void UpdateMouseInputs(); static void UpdateMouseWheel(); -static void UpdateTabFocus(); -static void UpdateDebugToolItemPicker(); static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); static void RenderWindowOuterBorders(ImGuiWindow* window); static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); +static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); +static void RenderDimmedBackgrounds(); +static ImGuiWindow* FindBlockingModal(ImGuiWindow* window); // Viewports static void UpdateViewportsNewFrame(); @@ -974,7 +1004,8 @@ static void* GImAllocatorUserData = NULL; ImGuiStyle::ImGuiStyle() { - Alpha = 1.0f; // Global alpha applies to everything in ImGui + Alpha = 1.0f; // Global alpha applies to everything in Dear ImGui. + DisabledAlpha = 0.60f; // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha. WindowPadding = ImVec2(8,8); // Padding within a window WindowRounding = 0.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. @@ -1059,8 +1090,8 @@ ImGuiIO::ImGuiIO() DisplaySize = ImVec2(-1.0f, -1.0f); DeltaTime = 1.0f / 60.0f; IniSavingRate = 5.0f; - IniFilename = "platform\\imgui.ini"; - LogFilename = "platform\\log\\imgui_log.txt"; + IniFilename = "platform\\layout.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables). + LogFilename = "platform\\logs\\imgui_log.txt"; MouseDoubleClickTime = 0.30f; MouseDoubleClickMaxDist = 6.0f; for (int i = 0; i < ImGuiKey_COUNT; i++) @@ -1166,6 +1197,24 @@ void ImGuiIO::ClearInputCharacters() InputQueueCharacters.resize(0); } +void ImGuiIO::ClearInputKeys() +{ + memset(KeysDown, 0, sizeof(KeysDown)); + for (int n = 0; n < IM_ARRAYSIZE(KeysDownDuration); n++) + KeysDownDuration[n] = KeysDownDurationPrev[n] = -1.0f; + KeyCtrl = KeyShift = KeyAlt = KeySuper = false; + KeyMods = KeyModsPrev = ImGuiKeyModFlags_None; + for (int n = 0; n < IM_ARRAYSIZE(NavInputsDownDuration); n++) + NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f; +} + +void ImGuiIO::AddFocusEvent(bool focused) +{ + // We intentionally overwrite this and process in NewFrame(), in order to give a chance + // to multi-viewports backends to queue AddFocusEvent(false),AddFocusEvent(true) in same frame. + AppFocusLost = !focused; +} + //----------------------------------------------------------------------------- // [SECTION] MISC HELPERS/UTILITIES (Geometry functions) //----------------------------------------------------------------------------- @@ -1695,7 +1744,7 @@ int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end) } // Based on stb_to_utf8() from github.com/nothings/stb/ -static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) +static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c) { if (c < 0x80) { @@ -1730,6 +1779,13 @@ static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) return 0; } +const char* ImTextCharToUtf8(char out_buf[5], unsigned int c) +{ + int count = ImTextCharToUtf8_inline(out_buf, 5, c); + out_buf[count] = 0; + return out_buf; +} + // Not optimal but we very rarely use this function. int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end) { @@ -1746,20 +1802,20 @@ static inline int ImTextCountUtf8BytesFromChar(unsigned int c) return 3; } -int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end) +int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end) { - char* buf_out = buf; - const char* buf_end = buf + buf_size; - while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) + char* buf_p = out_buf; + const char* buf_end = out_buf + out_buf_size; + while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) { unsigned int c = (unsigned int)(*in_text++); if (c < 0x80) - *buf_out++ = (char)c; + *buf_p++ = (char)c; else - buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end - buf_out - 1), c); + buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c); } - *buf_out = 0; - return (int)(buf_out - buf); + *buf_p = 0; + return (int)(buf_p - out_buf); } int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end) @@ -1894,7 +1950,7 @@ void ImGuiStorage::BuildSortByKey() { struct StaticFunc { - static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs) + static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs) { // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that. if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1; @@ -1902,8 +1958,7 @@ void ImGuiStorage::BuildSortByKey() return 0; } }; - if (Data.Size > 1) - ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairCompareByID); + ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairComparerByID); } int ImGuiStorage::GetInt(ImGuiID key, int default_val) const @@ -2195,9 +2250,10 @@ static bool GetSkipItemForListClipping() return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems); } -// Helper to calculate coarse clipping of large list of evenly sized items. -// NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. -// NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +// Legacy helper to calculate coarse clipping of large list of evenly sized items. +// This legacy API is not ideal because it assume we will return a single contiguous rectangle. +// Prefer using ImGuiListClipper which can returns non-contiguous ranges. void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) { ImGuiContext& g = *GImGui; @@ -2215,21 +2271,24 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items return; } - // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect - ImRect unclipped_rect = window->ClipRect; - if (g.NavMoveRequest) - unclipped_rect.Add(g.NavScoringRect); + // We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect + // We don't include g.NavId's rectangle in there (unless g.NavJustMovedToId is set) because the rectangle enlargement can get costly. + ImRect rect = window->ClipRect; + if (g.NavMoveScoringItems) + rect.Add(g.NavScoringNoClipRect); if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId) - unclipped_rect.Add(ImRect(window->Pos + window->NavRectRel[0].Min, window->Pos + window->NavRectRel[0].Max)); + rect.Add(WindowRectRelToAbs(window, window->NavRectRel[0])); // Could store and use NavJustMovedToRectRel const ImVec2 pos = window->DC.CursorPos; - int start = (int)((unclipped_rect.Min.y - pos.y) / items_height); - int end = (int)((unclipped_rect.Max.y - pos.y) / items_height); + int start = (int)((rect.Min.y - pos.y) / items_height); + int end = (int)((rect.Max.y - pos.y) / items_height); // When performing a navigation request, ensure we have one item extra in the direction we are moving to - if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up) + // FIXME: Verify this works with tabbing + const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); + if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) start--; - if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down) + if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) end++; start = ImClamp(start, 0, items_count); @@ -2237,17 +2296,42 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items *out_items_display_start = start; *out_items_display_end = end; } +#endif -static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height) +static void ImGuiListClipper_SortAndFuseRanges(ImVector& ranges, int offset = 0) +{ + if (ranges.Size - offset <= 1) + return; + + // Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries) + for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end) + for (int i = offset; i < sort_end + offset; ++i) + if (ranges[i].Min > ranges[i + 1].Min) + ImSwap(ranges[i], ranges[i + 1]); + + // Now fuse ranges together as much as possible. + for (int i = 1 + offset; i < ranges.Size; i++) + { + IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert); + if (ranges[i - 1].Max < ranges[i].Min) + continue; + ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min); + ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max); + ranges.erase(ranges.Data + i); + i--; + } +} + +static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height) { // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor. // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. - // The clipper should probably have a 4th step to display the last item in a regular manner. + // The clipper should probably have a final step to display the last item in a regular manner, maybe with an opt-out flag for data sets which may have costly seek? ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; float off_y = pos_y - window->DC.CursorPos.y; window->DC.CursorPos.y = pos_y; - window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y); + window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y); window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage. window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list. if (ImGuiOldColumns* columns = window->DC.CurrentColumns) @@ -2263,6 +2347,15 @@ static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height) } } +static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int item_n) +{ + // StartPosY starts from ItemsFrozen hence the subtraction + // Perform the add and multiply with double to allow seeking through larger ranges + ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData; + float pos_y = (float)((double)clipper->StartPosY + data->LossynessOffset + (double)(item_n - data->ItemsFrozen) * clipper->ItemsHeight); + ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, clipper->ItemsHeight); +} + ImGuiListClipper::ImGuiListClipper() { memset(this, 0, sizeof(*this)); @@ -2271,7 +2364,7 @@ ImGuiListClipper::ImGuiListClipper() ImGuiListClipper::~ImGuiListClipper() { - IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?"); + End(); } // Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1 @@ -2289,28 +2382,54 @@ void ImGuiListClipper::Begin(int items_count, float items_height) StartPosY = window->DC.CursorPos.y; ItemsHeight = items_height; ItemsCount = items_count; - ItemsFrozen = 0; - StepNo = 0; DisplayStart = -1; DisplayEnd = 0; + + // Acquire temporary buffer + if (++g.ClipperTempDataStacked > g.ClipperTempData.Size) + g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData()); + ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1]; + data->Reset(this); + data->LossynessOffset = window->DC.CursorStartPosLossyness.y; + TempData = data; } void ImGuiListClipper::End() { - if (ItemsCount < 0) // Already ended - return; - - // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user. - if (ItemsCount < INT_MAX && DisplayStart >= 0) - SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); + // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user. + ImGuiContext& g = *GImGui; + if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0) + ImGuiListClipper_SeekCursorForItem(this, ItemsCount); ItemsCount = -1; - StepNo = 3; + + // Restore temporary buffer and fix back pointers which may be invalidated when nesting + if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData) + { + IM_ASSERT(data->ListClipper == this); + data->StepNo = data->Ranges.Size; + if (--g.ClipperTempDataStacked > 0) + { + data = &g.ClipperTempData[g.ClipperTempDataStacked - 1]; + data->ListClipper->TempData = data; + } + TempData = NULL; + } +} + +void ImGuiListClipper::ForceDisplayRangeByIndices(int item_min, int item_max) +{ + ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; + IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet. + IM_ASSERT(item_min <= item_max); + if (item_min < item_max) + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_min, item_max)); } bool ImGuiListClipper::Step() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; ImGuiTable* table = g.CurrentTable; if (table && table->IsInsideRow) @@ -2318,95 +2437,117 @@ bool ImGuiListClipper::Step() // No items if (ItemsCount == 0 || GetSkipItemForListClipping()) + return (void)End(), false; + + // While we are in frozen row state, keep displaying items one by one, unclipped + // FIXME: Could be stored as a table-agnostic state. + if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows) { - End(); - return false; - } - - // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height) - if (StepNo == 0) - { - // While we are in frozen row state, keep displaying items one by one, unclipped - // FIXME: Could be stored as a table-agnostic state. - if (table != NULL && !table->IsUnfrozenRows) - { - DisplayStart = ItemsFrozen; - DisplayEnd = ItemsFrozen + 1; - ItemsFrozen++; - return true; - } - - StartPosY = window->DC.CursorPos.y; - if (ItemsHeight <= 0.0f) - { - // Submit the first item so we can measure its height (generally it is 0..1) - DisplayStart = ItemsFrozen; - DisplayEnd = ItemsFrozen + 1; - StepNo = 1; - return true; - } - - // Already has item height (given by user in Begin): skip to calculating step - DisplayStart = DisplayEnd; - StepNo = 2; - } - - // Step 1: the clipper infer height from first element - if (StepNo == 1) - { - IM_ASSERT(ItemsHeight <= 0.0f); - if (table) - { - const float pos_y1 = table->RowPosY1; // Using this instead of StartPosY to handle clipper straddling the frozen row - const float pos_y2 = table->RowPosY2; // Using this instead of CursorPos.y to take account of tallest cell. - ItemsHeight = pos_y2 - pos_y1; - window->DC.CursorPos.y = pos_y2; - } - else - { - ItemsHeight = window->DC.CursorPos.y - StartPosY; - } - IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); - StepNo = 2; - } - - // Reached end of list - if (DisplayEnd >= ItemsCount) - { - End(); - return false; - } - - // Step 2: calculate the actual range of elements to display, and position the cursor before the first element - if (StepNo == 2) - { - IM_ASSERT(ItemsHeight > 0.0f); - - int already_submitted = DisplayEnd; - ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd); - DisplayStart += already_submitted; - DisplayEnd += already_submitted; - - // Seek cursor - if (DisplayStart > already_submitted) - SetCursorPosYAndSetupForPrevLine(StartPosY + (DisplayStart - ItemsFrozen) * ItemsHeight, ItemsHeight); - - StepNo = 3; + DisplayStart = data->ItemsFrozen; + DisplayEnd = data->ItemsFrozen + 1; + if (DisplayStart >= ItemsCount) + return (void)End(), false; + data->ItemsFrozen++; return true; } - // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), - // Advance the cursor to the end of the list and then returns 'false' to end the loop. - if (StepNo == 3) + // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height) + bool calc_clipping = false; + if (data->StepNo == 0) { - // Seek cursor - if (ItemsCount < INT_MAX) - SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor - ItemsCount = -1; - return false; + StartPosY = window->DC.CursorPos.y; + if (ItemsHeight <= 0.0f) + { + // Submit the first item (or range) so we can measure its height (generally the first range is 0..1) + data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1)); + DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen); + DisplayEnd = ImMin(data->Ranges[0].Max, ItemsCount); + if (DisplayStart == DisplayEnd) + return (void)End(), false; + data->StepNo = 1; + return true; + } + calc_clipping = true; // If on the first step with known item height, calculate clipping. } - IM_ASSERT(0); + // Step 1: Let the clipper infer height from first range + if (ItemsHeight <= 0.0f) + { + IM_ASSERT(data->StepNo == 1); + if (table) + IM_ASSERT(table->RowPosY1 == StartPosY && table->RowPosY2 == window->DC.CursorPos.y); + + ItemsHeight = (window->DC.CursorPos.y - StartPosY) / (float)(DisplayEnd - DisplayStart); + bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); + if (affected_by_floating_point_precision) + ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries. + + IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); + calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards. + } + + // Step 0 or 1: Calculate the actual ranges of visible elements. + const int already_submitted = DisplayEnd; + if (calc_clipping) + { + if (g.LogEnabled) + { + // If logging is active, do not perform any clipping + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, ItemsCount)); + } + else + { + // Add range selected to be included for navigation + const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); + if (is_nav_request) + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0)); + if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && g.NavTabbingDir == -1) + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(ItemsCount - 1, ItemsCount)); + + // Add focused/active item + ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]); + if (g.NavId != 0 && window->NavLastIds[0] == g.NavId) + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0)); + + // Add visible range + const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0; + const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0; + data->Ranges.push_back(ImGuiListClipperRange::FromPositions(window->ClipRect.Min.y, window->ClipRect.Max.y, off_min, off_max)); + } + + // Convert position ranges to item index ranges + // - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping. + // - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list, + // which with the flooring/ceiling tend to lead to 2 items instead of one being submitted. + for (int i = 0; i < data->Ranges.Size; i++) + if (data->Ranges[i].PosToIndexConvert) + { + int m1 = (int)(((double)data->Ranges[i].Min - window->DC.CursorPos.y - data->LossynessOffset) / ItemsHeight); + int m2 = (int)((((double)data->Ranges[i].Max - window->DC.CursorPos.y - data->LossynessOffset) / ItemsHeight) + 0.999999f); + data->Ranges[i].Min = ImClamp(already_submitted + m1 + data->Ranges[i].PosToIndexOffsetMin, already_submitted, ItemsCount - 1); + data->Ranges[i].Max = ImClamp(already_submitted + m2 + data->Ranges[i].PosToIndexOffsetMax, data->Ranges[i].Min + 1, ItemsCount); + data->Ranges[i].PosToIndexConvert = false; + } + ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo); + } + + // Step 0+ (if item height is given in advance) or 1+: Display the next range in line. + if (data->StepNo < data->Ranges.Size) + { + DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted); + DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, ItemsCount); + if (DisplayStart > already_submitted) //-V1051 + ImGuiListClipper_SeekCursorForItem(this, DisplayStart); + data->StepNo++; + return true; + } + + // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), + // Advance the cursor to the end of the list and then returns 'false' to end the loop. + if (ItemsCount < INT_MAX) + ImGuiListClipper_SeekCursorForItem(this, ItemsCount); + ItemsCount = -1; + return false; } @@ -2496,6 +2637,7 @@ struct ImGuiStyleVarInfo static const ImGuiStyleVarInfo GStyleVarInfo[] = { { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha + { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize @@ -2773,7 +2915,7 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con int ellipsis_char_count = 1; if (ellipsis_char == (ImWchar)-1) { - ellipsis_char = (ImWchar)'.'; + ellipsis_char = font->DotChar; ellipsis_char_count = 3; } const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char); @@ -2914,8 +3056,7 @@ ImGuiWindow::~ImGuiWindow() { IM_ASSERT(DrawList == &DrawListInst); IM_DELETE(Name); - for (int i = 0; i != ColumnsStorage.Size; i++) - ColumnsStorage[i].~ImGuiOldColumns(); + ColumnsStorage.clear_destruct(); } ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) @@ -2923,10 +3064,9 @@ ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) ImGuiID seed = IDStack.back(); ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -2935,10 +3075,9 @@ ImGuiID ImGuiWindow::GetID(const void* ptr) ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); return id; } @@ -2947,10 +3086,9 @@ ImGuiID ImGuiWindow::GetID(int n) ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&n, sizeof(n), seed); ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); return id; } @@ -2958,10 +3096,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -2969,10 +3106,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&ptr, sizeof(void*), seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_Pointer, ptr); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL); return id; } @@ -2980,10 +3116,9 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n) { ImGuiID seed = IDStack.back(); ImGuiID id = ImHashData(&n, sizeof(n), seed); -#ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO(id, ImGuiDataType_S32, (intptr_t)n); -#endif + if (g.DebugHookIdInfo == id) + ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL); return id; } @@ -2991,7 +3126,7 @@ ImGuiID ImGuiWindow::GetIDNoKeepAlive(int n) ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) { ImGuiID seed = IDStack.back(); - const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) }; + ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs); ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed); ImGui::KeepAliveID(id); return id; @@ -3064,7 +3199,7 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) if (id) { g.ActiveIdIsAlive = id; - g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + g.ActiveIdSource = (g.NavActivateId == id || g.NavActivateInputId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; } // Clear declaration of inputs claimed by the widget @@ -3115,7 +3250,7 @@ void ImGui::MarkItemEdited(ImGuiID id) //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id); g.ActiveIdHasBeenEditedThisFrame = true; g.ActiveIdHasBeenEditedBefore = true; - g.CurrentWindow->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; } static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) @@ -3145,41 +3280,49 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (g.NavDisableMouseHover && !g.NavDisableHighlight) - return IsItemFocused(); + { + if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) + return false; + if (!IsItemFocused()) + return false; + } + else + { + // Test for bounding box overlap, as updated as ItemAdd() + ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags; + if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) + return false; + IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0); // Flags not supported by this function - // Test for bounding box overlap, as updated as ItemAdd() - ImGuiItemStatusFlags status_flags = window->DC.LastItemStatusFlags; - if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) - return false; - IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function + // Test if we are hovering the right window (our window could be behind another window) + // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) + // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable + // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was + // the test that has been running for a long while. + if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0) + if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0) + return false; - // Test if we are hovering the right window (our window could be behind another window) - // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) - // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable - // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was - // the test that has been running for a long while. - if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0) - if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0) + // Test if another item is active (e.g. being dragged) + if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) + if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) + return false; + + // Test if interactions on this window are blocked by an active popup or modal. + // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. + if (!IsWindowContentHoverable(window, flags)) return false; - // Test if another item is active (e.g. being dragged) - if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) - if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) + // Test if the item is disabled + if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) return false; - // Test if interactions on this window are blocked by an active popup or modal. - // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. - if (!IsWindowContentHoverable(window, flags)) - return false; + // Special handling for calling after Begin() which represent the title bar or tab. + // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. + if (g.LastItemData.ID == window->MoveId && window->WriteAccessed) + return false; + } - // Test if the item is disabled - if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) - return false; - - // Special handling for calling after Begin() which represent the title bar or tab. - // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. - if (window->DC.LastItemId == window->MoveId && window->WriteAccessed) - return false; return true; } @@ -3199,7 +3342,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) return false; if (g.NavDisableMouseHover) return false; - if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None) || (window->DC.ItemFlags & ImGuiItemFlags_Disabled)) + if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) { g.HoveredIdDisabled = true; return false; @@ -3208,9 +3351,21 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level // hover test in widgets code. We could also decide to split this function is two. if (id != 0) - { SetHoveredID(id); + // When disabled we'll return false but still set HoveredId + ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags); + if (item_flags & ImGuiItemFlags_Disabled) + { + // Release active id if turning disabled + if (g.ActiveId == id) + ClearActiveID(); + g.HoveredIdDisabled = true; + return false; + } + + if (id != 0) + { // [DEBUG] Item Picker tool! // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered @@ -3225,68 +3380,26 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) return true; } -bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged) +bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (!bb.Overlaps(window->ClipRect)) if (id == 0 || (id != g.ActiveId && id != g.NavId)) - if (clip_even_when_logged || !g.LogEnabled) + if (!g.LogEnabled) return true; return false; } // This is also inlined in ItemAdd() // Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set window->DC.LastItemDisplayRect! -void ImGui::SetLastItemData(ImGuiWindow* window, ImGuiID item_id, ImGuiItemStatusFlags item_flags, const ImRect& item_rect) -{ - window->DC.LastItemId = item_id; - window->DC.LastItemStatusFlags = item_flags; - window->DC.LastItemRect = item_rect; -} - -// Process TAB/Shift+TAB. Be mindful that this function may _clear_ the ActiveID when tabbing out. -bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id) +void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags item_flags, const ImRect& item_rect) { ImGuiContext& g = *GImGui; - - // Increment counters - const bool is_tab_stop = (window->DC.ItemFlags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; - window->DC.FocusCounterRegular++; - if (is_tab_stop) - window->DC.FocusCounterTabStop++; - - // Process TAB/Shift-TAB to tab *OUT* of the currently focused item. - // (Note that we can always TAB out of a widget that doesn't allow tabbing in) - if (g.ActiveId == id && g.TabFocusPressed && !IsActiveIdUsingKey(ImGuiKey_Tab) && g.TabFocusRequestNextWindow == NULL) - { - g.TabFocusRequestNextWindow = window; - g.TabFocusRequestNextCounterTabStop = window->DC.FocusCounterTabStop + (g.IO.KeyShift ? (is_tab_stop ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. - } - - // Handle focus requests - if (g.TabFocusRequestCurrWindow == window) - { - if (window->DC.FocusCounterRegular == g.TabFocusRequestCurrCounterRegular) - return true; - if (is_tab_stop && window->DC.FocusCounterTabStop == g.TabFocusRequestCurrCounterTabStop) - { - g.NavJustTabbedId = id; - return true; - } - - // If another item is about to be focused, we clear our own active id - if (g.ActiveId == id) - ClearActiveID(); - } - - return false; -} - -void ImGui::FocusableItemUnregister(ImGuiWindow* window) -{ - window->DC.FocusCounterRegular--; - window->DC.FocusCounterTabStop--; + g.LastItemData.ID = item_id; + g.LastItemData.InFlags = in_flags; + g.LastItemData.StatusFlags = item_flags; + g.LastItemData.Rect = item_rect; } float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) @@ -3512,8 +3625,9 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) FocusWindow(window); SetActiveID(window->MoveId, window); g.NavDisableHighlight = true; + g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos; g.ActiveIdNoClearOnFocusLoss = true; - g.ActiveIdClickOffset = g.IO.MousePos - window->RootWindow->Pos; + SetActiveIdUsingNavAndKeys(); bool can_move_window = true; if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) @@ -3549,8 +3663,8 @@ void ImGui::UpdateMouseMovingWindowNewFrame() } else { - ClearActiveID(); g.MovingWindow = NULL; + ClearActiveID(); } } else @@ -3614,7 +3728,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // Find the top-most window between HoveredWindow and the top-most Modal Window. // This is where we can trim the popup stack. ImGuiWindow* modal = GetTopMostPopupModal(); - bool hovered_window_above_modal = g.HoveredWindow && IsWindowAbove(g.HoveredWindow, modal); + bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal)); ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true); } } @@ -3630,13 +3744,15 @@ static void ImGui::UpdateMouseInputs() // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) if (IsMousePosValid(&g.IO.MousePos)) - g.IO.MousePos = g.LastValidMousePos = ImFloor(g.IO.MousePos); + g.IO.MousePos = g.MouseLastValidPos = ImFloor(g.IO.MousePos); // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; else g.IO.MouseDelta = ImVec2(0.0f, 0.0f); + + // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true. if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f) g.NavDisableMouseHover = false; @@ -3644,25 +3760,26 @@ static void ImGui::UpdateMouseInputs() for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) { g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; + g.IO.MouseClickedCount[i] = 0; // Will be filled below g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f; - g.IO.MouseDoubleClicked[i] = false; if (g.IO.MouseClicked[i]) { + bool is_repeated_click = false; if ((float)(g.Time - g.IO.MouseClickedTime[i]) < g.IO.MouseDoubleClickTime) { ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) - g.IO.MouseDoubleClicked[i] = true; - g.IO.MouseClickedTime[i] = -g.IO.MouseDoubleClickTime * 2.0f; // Mark as "old enough" so the third click isn't turned into a double-click + is_repeated_click = true; } + if (is_repeated_click) + g.IO.MouseClickedLastCount[i]++; else - { - g.IO.MouseClickedTime[i] = g.Time; - } + g.IO.MouseClickedLastCount[i] = 1; + g.IO.MouseClickedTime[i] = g.Time; g.IO.MouseClickedPos[i] = g.IO.MousePos; - g.IO.MouseDownWasDoubleClick[i] = g.IO.MouseDoubleClicked[i]; + g.IO.MouseClickedCount[i] = g.IO.MouseClickedLastCount[i]; g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; } @@ -3674,9 +3791,12 @@ static void ImGui::UpdateMouseInputs() g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x); g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); } - if (!g.IO.MouseDown[i] && !g.IO.MouseReleased[i]) - g.IO.MouseDownWasDoubleClick[i] = false; - if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation + + // We provide io.MouseDoubleClicked[] as a legacy service + g.IO.MouseDoubleClicked[i] = (g.IO.MouseClickedCount[i] == 2); + + // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation + if (g.IO.MouseClicked[i]) g.NavDisableMouseHover = false; } } @@ -3776,46 +3896,11 @@ void ImGui::UpdateMouseWheel() } } -void ImGui::UpdateTabFocus() -{ - ImGuiContext& g = *GImGui; - - // Pressing TAB activate widget focus - g.TabFocusPressed = (g.NavWindow && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)); - if (g.ActiveId == 0 && g.TabFocusPressed) - { - // Note that SetKeyboardFocusHere() sets the Next fields mid-frame. To be consistent we also - // manipulate the Next fields even, even though they will be turned into Curr fields by the code below. - g.TabFocusRequestNextWindow = g.NavWindow; - g.TabFocusRequestNextCounterRegular = INT_MAX; - if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) - g.TabFocusRequestNextCounterTabStop = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); - else - g.TabFocusRequestNextCounterTabStop = g.IO.KeyShift ? -1 : 0; - } - - // Turn queued focus request into current one - g.TabFocusRequestCurrWindow = NULL; - g.TabFocusRequestCurrCounterRegular = g.TabFocusRequestCurrCounterTabStop = INT_MAX; - if (g.TabFocusRequestNextWindow != NULL) - { - ImGuiWindow* window = g.TabFocusRequestNextWindow; - g.TabFocusRequestCurrWindow = window; - if (g.TabFocusRequestNextCounterRegular != INT_MAX && window->DC.FocusCounterRegular != -1) - g.TabFocusRequestCurrCounterRegular = ImModPositive(g.TabFocusRequestNextCounterRegular, window->DC.FocusCounterRegular + 1); - if (g.TabFocusRequestNextCounterTabStop != INT_MAX && window->DC.FocusCounterTabStop != -1) - g.TabFocusRequestCurrCounterTabStop = ImModPositive(g.TabFocusRequestNextCounterTabStop, window->DC.FocusCounterTabStop + 1); - g.TabFocusRequestNextWindow = NULL; - g.TabFocusRequestNextCounterRegular = g.TabFocusRequestNextCounterTabStop = INT_MAX; - } - - g.NavIdTabCounter = INT_MAX; -} - // The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app) void ImGui::UpdateHoveredWindowAndCaptureFlags() { ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING)); // Find the window hovered by mouse: @@ -3827,52 +3912,65 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindow, modal_window)) + if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window)) clear_hovered_windows = true; // Disabled mouse? - if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse) + if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) clear_hovered_windows = true; - // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward. - int mouse_earliest_button_down = -1; + // We track click ownership. When clicked outside of a window the click is owned by the application and + // won't report hovering nor request capture even while dragging over our windows afterward. + const bool has_open_popup = (g.OpenPopupStack.Size > 0); + const bool has_open_modal = (modal_window != NULL); + int mouse_earliest_down = -1; bool mouse_any_down = false; - for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { - if (g.IO.MouseClicked[i]) - g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0); - mouse_any_down |= g.IO.MouseDown[i]; - if (g.IO.MouseDown[i]) - if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down]) - mouse_earliest_button_down = i; + if (io.MouseClicked[i]) + { + io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup; + io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal; + } + mouse_any_down |= io.MouseDown[i]; + if (io.MouseDown[i]) + if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down]) + mouse_earliest_down = i; } - const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down]; + const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down]; + const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down]; // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; - if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload) + if (!mouse_avail && !mouse_dragging_extern_payload) clear_hovered_windows = true; if (clear_hovered_windows) g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL; - // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app) + // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app) + // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag if (g.WantCaptureMouseNextFrame != -1) - g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0); + { + io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0); + } else - g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0); + { + io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup; + io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal; + } - // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to Dear ImGui + app) + // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app) if (g.WantCaptureKeyboardNextFrame != -1) - g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); + io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); else - g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); - if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) - g.IO.WantCaptureKeyboard = true; + io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); + if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) + io.WantCaptureKeyboard = true; // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible - g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; + io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; } ImGuiKeyModFlags ImGui::GetMergedKeyModFlags() @@ -3916,7 +4014,8 @@ void ImGui::NewFrame() g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); - g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; + g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame)); + g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX; UpdateViewportsNewFrame(); @@ -3997,6 +4096,18 @@ void ImGui::NewFrame() g.DragDropWithinTarget = false; g.DragDropHoldJustPressedId = 0; + // Close popups on focus lost (currently wip/opt-in) + //if (g.IO.AppFocusLost) + // ClosePopupsExceptModals(); + + // Clear buttons state when focus is lost + // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) + if (g.IO.AppFocusLost) + { + g.IO.ClearInputKeys(); + g.IO.AppFocusLost = false; + } + // Update keyboard input state // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools g.IO.KeyMods = GetMergedKeyModFlags(); @@ -4030,9 +4141,6 @@ void ImGui::NewFrame() // Mouse wheel scrolling, scale UpdateMouseWheel(); - // Update legacy TAB focus - UpdateTabFocus(); - // Mark all windows as not visible and compact unused memory. IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size); const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; @@ -4053,6 +4161,9 @@ void ImGui::NewFrame() for (int i = 0; i < g.TablesLastTimeActive.Size; i++) if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time) TableGcCompactTransientBuffers(g.Tables.GetByIndex(i)); + for (int i = 0; i < g.TablesTempData.Size; i++) + if (g.TablesTempData[i].LastTimeActive >= 0.0f && g.TablesTempData[i].LastTimeActive < memory_compact_start_time) + TableGcCompactTransientBuffers(&g.TablesTempData[i]); if (g.GcCompactAll) GcCompactTransientMiscBuffers(); g.GcCompactAll = false; @@ -4066,12 +4177,12 @@ void ImGui::NewFrame() g.CurrentWindowStack.resize(0); g.BeginPopupStack.resize(0); g.ItemFlagsStack.resize(0); - g.ItemFlagsStack.push_back(ImGuiItemFlags_Default_); + g.ItemFlagsStack.push_back(ImGuiItemFlags_None); g.GroupStack.resize(0); - ClosePopupsOverWindow(g.NavWindow, false); - // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. + // [DEBUG] Update debug features UpdateDebugToolItemPicker(); + UpdateDebugToolStackQueries(); // Create implicit/fallback window - which we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. @@ -4084,31 +4195,6 @@ void ImGui::NewFrame() CallContextHooks(&g, ImGuiContextHookType_NewFramePost); } -// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. -void ImGui::UpdateDebugToolItemPicker() -{ - ImGuiContext& g = *GImGui; - g.DebugItemPickerBreakId = 0; - if (g.DebugItemPickerActive) - { - const ImGuiID hovered_id = g.HoveredIdPreviousFrame; - ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); - if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) - g.DebugItemPickerActive = false; - if (ImGui::IsMouseClicked(0) && hovered_id) - { - g.DebugItemPickerBreakId = hovered_id; - g.DebugItemPickerActive = false; - } - ImGui::SetNextWindowBgAlpha(0.60f); - ImGui::BeginTooltip(); - ImGui::Text("HoveredId: 0x%08X", hovered_id); - ImGui::Text("Press ESC to abort picking."); - ImGui::TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); - ImGui::EndTooltip(); - } -} - void ImGui::Initialize(ImGuiContext* context) { ImGuiContext& g = *context; @@ -4127,17 +4213,15 @@ void ImGui::Initialize(ImGuiContext* context) g.SettingsHandlers.push_back(ini_handler); } -#ifdef IMGUI_HAS_TABLE // Add .ini handle for ImGuiTable type TableSettingsInstallHandler(context); -#endif // #ifdef IMGUI_HAS_TABLE // Create default viewport ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); g.Viewports.push_back(viewport); #ifdef IMGUI_HAS_DOCK -#endif // #ifdef IMGUI_HAS_DOCK +#endif g.Initialized = true; } @@ -4170,9 +4254,7 @@ void ImGui::Shutdown(ImGuiContext* context) CallContextHooks(&g, ImGuiContextHookType_Shutdown); // Clear everything else - for (int i = 0; i < g.Windows.Size; i++) - IM_DELETE(g.Windows[i]); - g.Windows.clear(); + g.Windows.clear_delete(); g.WindowsFocusOrder.clear(); g.WindowsTempSortBuffer.clear(); g.CurrentWindow = NULL; @@ -4188,16 +4270,16 @@ void ImGui::Shutdown(ImGuiContext* context) g.OpenPopupStack.clear(); g.BeginPopupStack.clear(); - for (int i = 0; i < g.Viewports.Size; i++) - IM_DELETE(g.Viewports[i]); - g.Viewports.clear(); + g.Viewports.clear_delete(); g.TabBars.Clear(); g.CurrentTabBarStack.clear(); g.ShrinkWidthBuffer.clear(); + g.ClipperTempData.clear_destruct(); + g.Tables.Clear(); - g.CurrentTableStack.clear(); + g.TablesTempData.clear_destruct(); g.DrawChannelsTempMergeBuffer.clear(); g.ClipboardHandlerData.clear(); @@ -4238,8 +4320,7 @@ static void AddWindowToSortBuffer(ImVector* out_sorted_windows, Im if (window->Active) { int count = window->DC.ChildWindows.Size; - if (count > 1) - ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); + ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); for (int i = 0; i < count; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; @@ -4299,11 +4380,15 @@ static void AddWindowToDrawData(ImGuiWindow* window, int layer) } } -// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) -static void AddRootWindowToDrawData(ImGuiWindow* window) +static inline int GetWindowDisplayLayer(ImGuiWindow* window) { - int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; - AddWindowToDrawData(window, layer); + return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; +} + +// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) +static inline void AddRootWindowToDrawData(ImGuiWindow* window) +{ + AddWindowToDrawData(window, GetWindowDisplayLayer(window)); } void ImDrawDataBuilder::FlattenIntoSingleLayer() @@ -4362,6 +4447,84 @@ void ImGui::PopClipRect() window->ClipRect = window->DrawList->_ClipRectStack.back(); } +static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + ImGuiViewportP* viewport = (ImGuiViewportP*)GetMainViewport(); + ImRect viewport_rect = viewport->GetMainRect(); + + // Draw behind window by moving the draw command at the FRONT of the draw list + { + // We've already called AddWindowToDrawData() which called DrawList->ChannelsMerge() on DockNodeHost windows, + // and draw list have been trimmed already, hence the explicit recreation of a draw command if missing. + ImDrawList* draw_list = window->RootWindow->DrawList; + if (draw_list->CmdBuffer.Size == 0) + draw_list->AddDrawCmd(); + draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // Ensure ImDrawCmd are not merged + draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col); + ImDrawCmd cmd = draw_list->CmdBuffer.back(); + IM_ASSERT(cmd.ElemCount == 6); + draw_list->CmdBuffer.pop_back(); + draw_list->CmdBuffer.push_front(cmd); + draw_list->PopClipRect(); + } +} + +ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* bottom_most_visible_window = parent_window; + for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--) + { + ImGuiWindow* window = g.Windows[i]; + if (window->Flags & ImGuiWindowFlags_ChildWindow) + continue; + if (!IsWindowWithinBeginStackOf(window, parent_window)) + break; + if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window)) + bottom_most_visible_window = window; + } + return bottom_most_visible_window; +} + +static void ImGui::RenderDimmedBackgrounds() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal(); + const bool dim_bg_for_modal = (modal_window != NULL); + const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active); + if (!dim_bg_for_modal && !dim_bg_for_window_list) + return; + + if (dim_bg_for_modal) + { + // Draw dimming behind modal or a begin stack child, whichever comes first in draw order. + ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window); + RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(ImGuiCol_ModalWindowDimBg, g.DimBgRatio)); + } + else if (dim_bg_for_window_list) + { + // Draw dimming behind CTRL+Tab target window + RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); + + // Draw border around CTRL+Tab target window + ImGuiWindow* window = g.NavWindowingTargetAnim; + ImGuiViewport* viewport = GetMainViewport(); + float distance = g.FontSize; + ImRect bb = window->Rect(); + bb.Expand(distance); + if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y) + bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward + if (window->DrawList->CmdBuffer.Size == 0) + window->DrawList->AddDrawCmd(); + window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); + window->DrawList->PopClipRect(); + } +} + // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal. void ImGui::EndFrame() { @@ -4440,11 +4603,15 @@ void ImGui::EndFrame() // Clear Input data for next frame g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; g.IO.InputQueueCharacters.resize(0); + g.IO.KeyModsPrev = g.IO.KeyMods; // doing it here is better than in NewFrame() as we'll tolerate backend writing to KeyMods. If we want to firmly disallow it we should detect it. memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); CallContextHooks(&g, ImGuiContextHookType_EndFramePost); } +// Prepare the data for rendering so you can call GetDrawData() +// (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all: +// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend) void ImGui::Render() { ImGuiContext& g = *GImGui; @@ -4452,6 +4619,7 @@ void ImGui::Render() if (g.FrameCountEnded != g.FrameCount) EndFrame(); + const bool first_render_of_frame = (g.FrameCountRendered != g.FrameCount); g.FrameCountRendered = g.FrameCount; g.IO.MetricsRenderWindows = 0; @@ -4466,6 +4634,10 @@ void ImGui::Render() AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); } + // Draw modal/window whitening backgrounds + if (first_render_of_frame) + RenderDimmedBackgrounds(); + // Add ImDrawList to render ImGuiWindow* windows_to_render_top_most[2]; windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; @@ -4473,6 +4645,7 @@ void ImGui::Render() for (int n = 0; n != g.Windows.Size; n++) { ImGuiWindow* window = g.Windows[n]; + IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1]) AddRootWindowToDrawData(window); } @@ -4488,7 +4661,7 @@ void ImGui::Render() viewport->DrawDataBuilder.FlattenIntoSingleLayer(); // Draw software mouse cursor if requested by io.MouseDrawCursor flag - if (g.IO.MouseDrawCursor) + if (g.IO.MouseDrawCursor && first_render_of_frame) RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); // Add foreground ImDrawList (for each active viewport) @@ -4550,6 +4723,7 @@ static void FindHoveredWindow() for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* window = g.Windows[i]; + IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. if (!window->Active || window->Hidden) continue; if (window->Flags & ImGuiWindowFlags_NoMouseInputs) @@ -4576,6 +4750,7 @@ static void FindHoveredWindow() if (hovered_window == NULL) hovered_window = window; + IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow)) hovered_window_ignoring_moving_window = window; if (hovered_window && hovered_window_ignoring_moving_window) @@ -4709,7 +4884,14 @@ bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseDoubleClicked[button]; + return g.IO.MouseClickedCount[button] == 2; +} + +int ImGui::GetMouseClickedCount(ImGuiMouseButton button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseClickedCount[button]; } // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. @@ -4815,10 +4997,7 @@ bool ImGui::IsItemActive() { ImGuiContext& g = *GImGui; if (g.ActiveId) - { - ImGuiWindow* window = g.CurrentWindow; - return g.ActiveId == window->DC.LastItemId; - } + return g.ActiveId == g.LastItemData.ID; return false; } @@ -4826,21 +5005,17 @@ bool ImGui::IsItemActivated() { ImGuiContext& g = *GImGui; if (g.ActiveId) - { - ImGuiWindow* window = g.CurrentWindow; - if (g.ActiveId == window->DC.LastItemId && g.ActiveIdPreviousFrame != window->DC.LastItemId) + if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID) return true; - } return false; } bool ImGui::IsItemDeactivated() { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDeactivated) - return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Deactivated) != 0; - return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId); + if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated) + return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0; + return (g.ActiveIdPreviousFrame == g.LastItemData.ID && g.ActiveIdPreviousFrame != 0 && g.ActiveId != g.LastItemData.ID); } bool ImGui::IsItemDeactivatedAfterEdit() @@ -4853,9 +5028,7 @@ bool ImGui::IsItemDeactivatedAfterEdit() bool ImGui::IsItemFocused() { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - if (g.NavId != window->DC.LastItemId || g.NavId == 0) + if (g.NavId != g.LastItemData.ID || g.NavId == 0) return false; return true; } @@ -4870,13 +5043,13 @@ bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button) bool ImGui::IsItemToggledOpen() { ImGuiContext& g = *GImGui; - return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false; + return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false; } bool ImGui::IsItemToggledSelection() { ImGuiContext& g = *GImGui; - return (g.CurrentWindow->DC.LastItemStatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; + return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; } bool ImGui::IsAnyItemHovered() @@ -4899,14 +5072,14 @@ bool ImGui::IsAnyItemFocused() bool ImGui::IsItemVisible() { - ImGuiWindow* window = GetCurrentWindowRead(); - return window->ClipRect.Overlaps(window->DC.LastItemRect); + ImGuiContext& g = *GImGui; + return g.CurrentWindow->ClipRect.Overlaps(g.LastItemData.Rect); } bool ImGui::IsItemEdited() { - ImGuiWindow* window = GetCurrentWindowRead(); - return (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_Edited) != 0; + ImGuiContext& g = *GImGui; + return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0; } // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. @@ -4914,7 +5087,7 @@ bool ImGui::IsItemEdited() void ImGui::SetItemAllowOverlap() { ImGuiContext& g = *GImGui; - ImGuiID id = g.CurrentWindow->DC.LastItemId; + ImGuiID id = g.LastItemData.ID; if (g.HoveredId == id) g.HoveredIdAllowOverlap = true; if (g.ActiveId == id) @@ -4924,29 +5097,39 @@ void ImGui::SetItemAllowOverlap() void ImGui::SetItemUsingMouseWheel() { ImGuiContext& g = *GImGui; - ImGuiID id = g.CurrentWindow->DC.LastItemId; + ImGuiID id = g.LastItemData.ID; if (g.HoveredId == id) g.HoveredIdUsingMouseWheel = true; if (g.ActiveId == id) g.ActiveIdUsingMouseWheel = true; } +void ImGui::SetActiveIdUsingNavAndKeys() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.ActiveId != 0); + g.ActiveIdUsingNavDirMask = ~(ImU32)0; + g.ActiveIdUsingNavInputMask = ~(ImU32)0; + g.ActiveIdUsingKeyInputMask = ~(ImU64)0; + NavMoveRequestCancel(); +} + ImVec2 ImGui::GetItemRectMin() { - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.LastItemRect.Min; + ImGuiContext& g = *GImGui; + return g.LastItemData.Rect.Min; } ImVec2 ImGui::GetItemRectMax() { - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.LastItemRect.Max; + ImGuiContext& g = *GImGui; + return g.LastItemData.Rect.Max; } ImVec2 ImGui::GetItemRectSize() { - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.LastItemRect.GetSize(); + ImGuiContext& g = *GImGui; + return g.LastItemData.Rect.GetSize(); } bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags) @@ -4989,7 +5172,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b parent_window->DC.CursorPos = child_window->Pos; // Process navigation-in immediately so NavInit can run on first frame - if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll)) + if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavHasScroll)) { FocusWindow(child_window); NavInitWindow(child_window, false); @@ -5036,13 +5219,13 @@ void ImGui::EndChild() ImGuiWindow* parent_window = g.CurrentWindow; ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); ItemSize(sz); - if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) + if ((window->DC.NavLayersActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) { ItemAdd(bb, window->ChildId); RenderNavHighlight(bb, window->ChildId); - // When browsing a window that has no activable items (scroll only) we keep a highlight on the child - if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow) + // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying) + if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow) RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin); } else @@ -5051,7 +5234,7 @@ void ImGui::EndChild() ItemAdd(bb, 0); } if (g.HoveredWindow == window) - parent_window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; } g.WithinEndChild = false; g.LogLinePosY = -FLT_MAX; // To enforce a carriage return @@ -5104,6 +5287,29 @@ static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settin window->Collapsed = settings->Collapsed; } +static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags) +{ + ImGuiContext& g = *GImGui; + + const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0; + const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild; + if ((just_created || child_flag_changed) && !new_is_explicit_child) + { + IM_ASSERT(!g.WindowsFocusOrder.contains(window)); + g.WindowsFocusOrder.push_back(window); + window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); + } + else if (!just_created && child_flag_changed && new_is_explicit_child) + { + IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window); + for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++) + g.WindowsFocusOrder[n]->FocusOrder--; + g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder); + window->FocusOrder = -1; + } + window->IsExplicitChild = new_is_explicit_child; +} + static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; @@ -5143,16 +5349,12 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); } - if (!(flags & ImGuiWindowFlags_ChildWindow)) - { - g.WindowsFocusOrder.push_back(window); - window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1); - } - if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) g.Windows.push_front(window); // Quite slow but rare and only once else g.Windows.push_back(window); + UpdateWindowInFocusOrderList(window, true, window->Flags); + return window; } @@ -5259,11 +5461,11 @@ ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window) return size_final; } -static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags) +static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window) { - if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) + if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) return ImGuiCol_PopupBg; - if (flags & ImGuiWindowFlags_ChildWindow) + if (window->Flags & ImGuiWindowFlags_ChildWindow) return ImGuiCol_ChildBg; return ImGuiCol_WindowBg; } @@ -5388,7 +5590,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; - if (held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0) + if (held && g.IO.MouseClickedCount[0] == 2 && resize_grip_n == 0) { // Manual auto-fit when double-clicking size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit); @@ -5418,7 +5620,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s bool hovered, held; ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING); ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID() - ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren); + ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) { @@ -5446,7 +5648,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s { ImVec2 nav_resize_delta; if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift) - nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); + nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_RawKeyboard, ImGuiInputReadMode_Down); if (g.NavInputSource == ImGuiInputSource_Gamepad) nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down); if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) @@ -5541,7 +5743,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar // Window background if (!(flags & ImGuiWindowFlags_NoBackground)) { - ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); + ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window)); bool override_alpha = false; float alpha = 1.0f; if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha) @@ -5607,8 +5809,9 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None); // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer) - const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; + // FIXME-NAV: Might want (or not?) to set the equivalent of ImGuiButtonFlags_NoNavFocus so that mouse clicks on standard title bar items don't necessarily set nav/keyboard ref? + const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; // Layout buttons @@ -5645,12 +5848,11 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl *p_open = false; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; - window->DC.ItemFlags = item_flags_backup; + g.CurrentItemFlags = item_flags_backup; // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker) // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code.. - const char* UNSAVED_DOCUMENT_MARKER = "*"; - const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f; + const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f; const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button, @@ -5669,23 +5871,30 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y); ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y); + if (flags & ImGuiWindowFlags_UnsavedDocument) + { + ImVec2 marker_pos; + marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x); + marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f; + if (marker_pos.x > layout_r.Min.x) + { + RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text)); + clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f)); + } + } //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r); - if (flags & ImGuiWindowFlags_UnsavedDocument) - { - ImVec2 marker_pos = ImVec2(ImMax(layout_r.Min.x, layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, layout_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f); - ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f)); - RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r); - } } void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) { window->ParentWindow = parent_window; - window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; + window->RootWindow = window->RootWindowPopupTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) window->RootWindow = parent_window->RootWindow; + if (parent_window && (flags & ImGuiWindowFlags_Popup)) + window->RootWindowPopupTree = parent_window->RootWindowPopupTree; if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) @@ -5695,6 +5904,36 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags } } +// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing) +// should be positioned behind that modal window, unless the window was created inside the modal begin-stack. +// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent. +// - Window // FindBlockingModal() returns Modal1 +// - Window // .. returns Modal1 +// - Modal1 // .. returns Modal2 +// - Window // .. returns Modal2 +// - Window // .. returns Modal2 +// - Modal2 // .. returns Modal2 +static ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (g.OpenPopupStack.Size <= 0) + return NULL; + + // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal. + for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--) + { + ImGuiWindow* popup_window = g.OpenPopupStack.Data[i].Window; + if (popup_window == NULL || !popup_window->WasActive || !(popup_window->Flags & ImGuiWindowFlags_Modal)) // Check WasActive, because this code may run before popup renders on current frame. + continue; + if (IsWindowWithinBeginStackOf(window, popup_window)) // Window is rendered over last modal, no render order change needed. + break; + for (ImGuiWindow* parent = popup_window->ParentWindowInBeginStack->RootWindow; parent != NULL; parent = parent->ParentWindowInBeginStack->RootWindow) + if (IsWindowWithinBeginStackOf(window, parent)) + return popup_window; // Place window above its begin stack parent. + } + return NULL; +} + // Push a new Dear ImGui window to add widgets to. // - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. // - Begin/End can be called multiple times during the frame with the same window name to append content. @@ -5715,6 +5954,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const bool window_just_created = (window == NULL); if (window_just_created) window = CreateNewWindow(name, flags); + else + UpdateWindowInFocusOrderList(window, window_just_created, flags); // Automatically disable manual moving/resizing when NoInputs is set if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) @@ -5754,7 +5995,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack - ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); + ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window; ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); @@ -5764,10 +6005,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Add to stack // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() - g.CurrentWindowStack.push_back(window); g.CurrentWindow = window; - window->DC.StackSizesOnBegin.SetToCurrentState(); + ImGuiWindowStackData window_stack_data; + window_stack_data.Window = window; + window_stack_data.ParentLastItemDataBackup = g.LastItemData; + window_stack_data.StackSizesOnBegin.SetToCurrentState(); + g.CurrentWindowStack.push_back(window_stack_data); g.CurrentWindow = NULL; + if (flags & ImGuiWindowFlags_ChildMenu) + g.BeginMenuCount++; if (flags & ImGuiWindowFlags_Popup) { @@ -5779,7 +6025,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update ->RootWindow and others pointers (before any possible call to FocusWindow) if (first_begin_of_the_frame) + { UpdateWindowParentAndRootLinks(window, flags, parent_window); + window->ParentWindowInBeginStack = parent_window_in_stack; + } // Process SetNextWindow***() calls // (FIXME: Consider splitting the HasXXX flags into X/Y components @@ -5914,7 +6163,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar. ImRect title_bar_rect = window->TitleBarRect(); - if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0]) + if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseClickedCount[0] == 2) window->WantCollapseToggle = true; if (window->WantCollapseToggle) { @@ -6034,6 +6283,22 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) want_focus = true; else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) want_focus = true; + + ImGuiWindow* modal = GetTopMostPopupModal(); + if (modal != NULL && !IsWindowWithinBeginStackOf(window, modal)) + { + // Avoid focusing a window that is created outside of active modal. This will prevent active modal from being closed. + // Since window is not focused it would reappear at the same display position like the last time it was visible. + // In case of completely new windows it would go to the top (over current modal), but input to such window would still be blocked by modal. + // Position window behind a modal that is not a begin-parent of this window. + want_focus = false; + if (window == window->RootWindow) + { + ImGuiWindow* blocking_modal = FindBlockingModal(window); + IM_ASSERT(blocking_modal != NULL); + BringWindowToDisplayBehind(window, blocking_modal); + } + } } // Handle manual resize: Resize Grips, Borders, Gamepad @@ -6084,7 +6349,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Inner rectangle // Not affected by window border size. Used by: // - InnerClipRect - // - ScrollToBringRectIntoView() + // - ScrollToRectEx() // - NavUpdatePageUpPageDown() // - Scrollbar() window->InnerRect.Min.x = window->Pos.x; @@ -6131,34 +6396,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); PushClipRect(host_rect.Min, host_rect.Max, false); - // Draw modal window background (darkens what is behind them, all viewports) - const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0; - const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow); - if (dim_bg_for_modal || dim_bg_for_window_list) - { - const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); - window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, dim_bg_col); - } - - // Draw navigation selection/windowing rectangle background - if (dim_bg_for_window_list && window == g.NavWindowingTargetAnim) - { - ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); - } - - // Since 1.71, child window can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call. + // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71) // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. - // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child. - // We also disabled this when we have dimming overlay behind this specific one child. - // FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected. + // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493) { bool render_decorations_in_parent = false; if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) - if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_window->DrawList->VtxBuffer.Size > 0) + { + // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here) + // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs + ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL; + bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false; + bool parent_is_empty = parent_window->DrawList->VtxBuffer.Size > 0; + if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_is_empty && !previous_child_overlapping) render_decorations_in_parent = true; + } if (render_decorations_in_parent) window->DrawList = parent_window->DrawList; @@ -6171,20 +6423,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList = &window->DrawListInst; } - // Draw navigation selection/windowing rectangle border - if (g.NavWindowingTargetAnim == window) - { - float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); - ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward - { - bb.Expand(-g.FontSize - 1.0f); - rounding = window->WindowRounding; - } - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, 0, 3.0f); - } - // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING) // Work rectangle. @@ -6216,7 +6454,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x; window->DC.GroupOffset.x = 0.0f; window->DC.ColumnsOffset.x = 0.0f; - window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.Indent.x + window->DC.ColumnsOffset.x, decoration_up_height + window->WindowPadding.y - window->Scroll.y); + + // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount. + // This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64. + double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DC.ColumnsOffset.x; + double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + decoration_up_height; + window->DC.CursorStartPos = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y); + window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y)); window->DC.CursorPos = window->DC.CursorStartPos; window->DC.CursorPosPrevLine = window->DC.CursorPos; window->DC.CursorMaxPos = window->DC.CursorStartPos; @@ -6225,13 +6469,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; - window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext; - window->DC.NavLayerActiveMaskNext = 0x00; + window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext; + window->DC.NavLayersActiveMaskNext = 0x00; window->DC.NavHideHighlightOneFrame = false; window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); window->DC.MenuBarAppending = false; - window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user); + window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user); window->DC.TreeDepth = 0; window->DC.TreeJumpToParentOnPopMask = 0x00; window->DC.ChildWindows.resize(0); @@ -6239,7 +6483,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.CurrentColumns = NULL; window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; - window->DC.FocusCounterRegular = window->DC.FocusCounterTabStop = -1; window->DC.ItemWidth = window->ItemWidthDefault; window->DC.TextWrapPos = -1.0f; // disabled @@ -6277,11 +6520,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). // This is useful to allow creating context menus on title bar only, etc. - SetLastItemData(window, window->MoveId, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect); + SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect); #ifdef IMGUI_ENABLE_TEST_ENGINE if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) - IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId); + IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.Rect, g.LastItemData.ID); #endif } else @@ -6291,8 +6534,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Pull/inherit current state - window->DC.ItemFlags = g.ItemFlagsStack.back(); // Inherit from shared stack - window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // Inherit from parent only // -V595 + window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : window->GetID("#FOCUSSCOPE"); // Inherit from parent only // -V595 PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); @@ -6310,9 +6552,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow?? - if (!g.LogEnabled) + { + const bool nav_request = (flags & ImGuiWindowFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); + if (!g.LogEnabled && !nav_request) if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) window->HiddenFramesCanSkipItems = 1; + } // Hide along with parent or if parent is collapsed if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0)) @@ -6326,7 +6571,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->HiddenFramesCanSkipItems = 1; // Update the Hidden flag - window->Hidden = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0) || (window->HiddenFramesForRenderOnly > 0); + bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0); + window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0); // Disable inputs for requested number of frames if (window->DisableInputsFrames > 0) @@ -6337,7 +6583,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update the SkipItems flag, used to early out of all items functions (no layout required) bool skip_items = false; - if (window->Collapsed || !window->Active || window->Hidden) + if (window->Collapsed || !window->Active || hidden_regular) if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) skip_items = true; window->SkipItems = skip_items; @@ -6373,11 +6619,14 @@ void ImGui::End() LogFinish(); // Pop from window stack - g.CurrentWindowStack.pop_back(); + g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup; + if (window->Flags & ImGuiWindowFlags_ChildMenu) + g.BeginMenuCount--; if (window->Flags & ImGuiWindowFlags_Popup) g.BeginPopupStack.pop_back(); - window->DC.StackSizesOnBegin.CompareWithCurrentState(); - SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); + g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithCurrentState(); + g.CurrentWindowStack.pop_back(); + SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window); } void ImGui::BringWindowToFocusFront(ImGuiWindow* window) @@ -6430,6 +6679,34 @@ void ImGui::BringWindowToDisplayBack(ImGuiWindow* window) } } +void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window) +{ + IM_ASSERT(window != NULL && behind_window != NULL); + ImGuiContext& g = *GImGui; + window = window->RootWindow; + behind_window = behind_window->RootWindow; + int pos_wnd = FindWindowDisplayIndex(window); + int pos_beh = FindWindowDisplayIndex(behind_window); + if (pos_wnd < pos_beh) + { + size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*); + memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes); + g.Windows[pos_beh - 1] = window; + } + else + { + size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*); + memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes); + g.Windows[pos_beh] = window; + } +} + +int ImGui::FindWindowDisplayIndex(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + return g.Windows.index_from_ptr(g.Windows.find(window)); +} + // Moving window to front of display and set focus (which happens to be back of our sorted list) void ImGui::FocusWindow(ImGuiWindow* window) { @@ -6444,7 +6721,7 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavFocusScopeId = 0; g.NavIdIsAlive = false; g.NavLayer = ImGuiNavLayer_Main; - g.NavInitRequest = g.NavMoveRequest = false; + g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false; NavUpdateAnyRequestFlag(); //IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL); } @@ -6477,8 +6754,18 @@ void ImGui::FocusWindow(ImGuiWindow* window) void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window) { ImGuiContext& g = *GImGui; - - const int start_idx = ((under_this_window != NULL) ? FindWindowFocusIndex(under_this_window) : g.WindowsFocusOrder.Size) - 1; + int start_idx = g.WindowsFocusOrder.Size - 1; + if (under_this_window != NULL) + { + // Aim at root window behind us, if we are in a child window that's our own root (see #4640) + int offset = -1; + while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow) + { + under_this_window = under_this_window->ParentWindow; + offset = 0; + } + start_idx = FindWindowFocusIndex(under_this_window) + offset; + } for (int i = start_idx; i >= 0; i--) { // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. @@ -6533,24 +6820,56 @@ void ImGui::PopFont() void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiItemFlags item_flags = window->DC.ItemFlags; + ImGuiItemFlags item_flags = g.CurrentItemFlags; IM_ASSERT(item_flags == g.ItemFlagsStack.back()); if (enabled) item_flags |= option; else item_flags &= ~option; - window->DC.ItemFlags = item_flags; + g.CurrentItemFlags = item_flags; g.ItemFlagsStack.push_back(item_flags); } void ImGui::PopItemFlag() { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack. g.ItemFlagsStack.pop_back(); - window->DC.ItemFlags = g.ItemFlagsStack.back(); + g.CurrentItemFlags = g.ItemFlagsStack.back(); +} + +// BeginDisabled()/EndDisabled() +// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) +// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently. +// - Feedback welcome at https://github.com/ocornut/imgui/issues/211 +// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. +// - Optimized shortcuts instead of PushStyleVar() + PushItemFlag() +void ImGui::BeginDisabled(bool disabled) +{ + ImGuiContext& g = *GImGui; + bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; + if (!was_disabled && disabled) + { + g.DisabledAlphaBackup = g.Style.Alpha; + g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha); + } + if (was_disabled || disabled) + g.CurrentItemFlags |= ImGuiItemFlags_Disabled; + g.ItemFlagsStack.push_back(g.CurrentItemFlags); + g.DisabledStackSize++; +} + +void ImGui::EndDisabled() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.DisabledStackSize > 0); + g.DisabledStackSize--; + bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; + //PopItemFlag(); + g.ItemFlagsStack.pop_back(); + g.CurrentItemFlags = g.ItemFlagsStack.back(); + if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0) + g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar(); } // FIXME: Look into renaming this once we have settled the new Focus/Activation/TabStop system. @@ -6588,7 +6907,36 @@ void ImGui::PopTextWrapPos() window->DC.TextWrapPosStack.pop_back(); } -bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) +static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy) +{ + ImGuiWindow* last_window = NULL; + while (last_window != window) + { + last_window = window; + window = window->RootWindow; + if (popup_hierarchy) + window = window->RootWindowPopupTree; + } + return window; +} + +bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy) +{ + ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy); + if (window_root == potential_parent) + return true; + while (window != NULL) + { + if (window == potential_parent) + return true; + if (window == window_root) // end of chain + return false; + window = window->ParentWindow; + } + return false; +} + +bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent) { if (window->RootWindow == potential_parent) return true; @@ -6596,7 +6944,7 @@ bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) { if (window == potential_parent) return true; - window = window->ParentWindow; + window = window->ParentWindowInBeginStack; } return false; } @@ -6604,6 +6952,12 @@ bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below) { ImGuiContext& g = *GImGui; + + // It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array + const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below); + if (display_layer_delta != 0) + return display_layer_delta > 0; + for (int i = g.Windows.Size - 1; i >= 0; i--) { ImGuiWindow* candidate_window = g.Windows[i]; @@ -6617,39 +6971,33 @@ bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_b bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) { - IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function + IM_ASSERT((flags & (ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled)) == 0); // Flags not supported by this function ImGuiContext& g = *GImGui; - if (g.HoveredWindow == NULL) + ImGuiWindow* ref_window = g.HoveredWindow; + ImGuiWindow* cur_window = g.CurrentWindow; + if (ref_window == NULL) return false; if ((flags & ImGuiHoveredFlags_AnyWindow) == 0) { - ImGuiWindow* window = g.CurrentWindow; - switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) - { - case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows: - if (g.HoveredWindow->RootWindow != window->RootWindow) - return false; - break; - case ImGuiHoveredFlags_RootWindow: - if (g.HoveredWindow != window->RootWindow) - return false; - break; - case ImGuiHoveredFlags_ChildWindows: - if (!IsWindowChildOf(g.HoveredWindow, window)) - return false; - break; - default: - if (g.HoveredWindow != window) - return false; - break; - } + IM_ASSERT(cur_window); // Not inside a Begin()/End() + const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0; + if (flags & ImGuiHoveredFlags_RootWindow) + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); + + bool result; + if (flags & ImGuiHoveredFlags_ChildWindows) + result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy); + else + result = (ref_window == cur_window); + if (!result) + return false; } - if (!IsWindowContentHoverable(g.HoveredWindow, flags)) + if (!IsWindowContentHoverable(ref_window, flags)) return false; if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId) + if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId) return false; return true; } @@ -6657,22 +7005,23 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) { ImGuiContext& g = *GImGui; + ImGuiWindow* ref_window = g.NavWindow; + ImGuiWindow* cur_window = g.CurrentWindow; + if (ref_window == NULL) + return false; if (flags & ImGuiFocusedFlags_AnyWindow) - return g.NavWindow != NULL; + return true; - IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() - switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) - { - case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; - case ImGuiFocusedFlags_RootWindow: - return g.NavWindow == g.CurrentWindow->RootWindow; - case ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow); - default: - return g.NavWindow == g.CurrentWindow; - } + IM_ASSERT(cur_window); // Not inside a Begin()/End() + const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0; + if (flags & ImGuiHoveredFlags_RootWindow) + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); + + if (flags & ImGuiHoveredFlags_ChildWindows) + return IsWindowChildOf(ref_window, cur_window, popup_hierarchy); + else + return (ref_window == cur_window); } // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) @@ -6942,6 +7291,7 @@ void ImGui::ActivateItem(ImGuiID id) { ImGuiContext& g = *GImGui; g.NavNextActivateId = id; + g.NavNextActivateFlags = ImGuiActivateFlags_None; } void ImGui::PushFocusScope(ImGuiID id) @@ -6963,12 +7313,21 @@ void ImGui::PopFocusScope() void ImGui::SetKeyboardFocusHere(int offset) { - IM_ASSERT(offset >= -1); // -1 is allowed but not below ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - g.TabFocusRequestNextWindow = window; - g.TabFocusRequestNextCounterRegular = window->DC.FocusCounterRegular + 1 + offset; - g.TabFocusRequestNextCounterTabStop = INT_MAX; + IM_ASSERT(offset >= -1); // -1 is allowed but not below + g.NavWindow = window; + ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; + NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, ImGuiNavMoveFlags_Tabbing | ImGuiNavMoveFlags_FocusApi, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. + if (offset == -1) + { + NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal); + } + else + { + g.NavTabbingDir = 1; + g.NavTabbingCounter = offset + 1; + } } void ImGui::SetItemDefaultFocus() @@ -6977,15 +7336,17 @@ void ImGui::SetItemDefaultFocus() ImGuiWindow* window = g.CurrentWindow; if (!window->Appearing) return; - if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) - { - g.NavInitRequest = false; - g.NavInitResultId = g.NavWindow->DC.LastItemId; - g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); - NavUpdateAnyRequestFlag(); - if (!IsItemVisible()) - SetScrollHereY(); - } + if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResultId == 0) || g.NavLayer != window->DC.NavLayerCurrent) + return; + + g.NavInitRequest = false; + g.NavInitResultId = g.LastItemData.ID; + g.NavInitResultRectRel = WindowRectAbsToRel(window, g.LastItemData.Rect); + NavUpdateAnyRequestFlag(); + + // Scroll could be done in NavInitRequestApplyResult() via a opt-in flag (we however don't want regular init requests to scroll) + if (!IsItemVisible()) + ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None); } void ImGui::SetStateStorage(ImGuiStorage* tree) @@ -7037,6 +7398,8 @@ void ImGui::PushOverrideID(ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + if (g.DebugHookIdInfo == id) + DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL); window->IDStack.push_back(id); } @@ -7046,11 +7409,10 @@ void ImGui::PushOverrideID(ImGuiID id) ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed) { ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed); - ImGui::KeepAliveID(id); -#ifdef IMGUI_ENABLE_TEST_ENGINE + KeepAliveID(id); ImGuiContext& g = *GImGui; - IMGUI_TEST_ENGINE_ID_INFO2(id, ImGuiDataType_String, str, str_end); -#endif + if (g.DebugHookIdInfo == id) + DebugHookIdInfo(id, ImGuiDataType_String, str, str_end); return id; } @@ -7132,8 +7494,7 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!"); IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!"); - IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?"); - IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?"); + IM_ASSERT(g.IO.Fonts->IsBuilt() && "Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()"); IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!"); IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations @@ -7196,55 +7557,13 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi { // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations" ImGuiContext& g = *GImGui; - while (g.CurrentWindowStack.Size > 0) + while (g.CurrentWindowStack.Size > 0) //-V1044 { -#ifdef IMGUI_HAS_TABLE - while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) - { - if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name); - EndTable(); - } -#endif + ErrorCheckEndWindowRecover(log_callback, user_data); ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(window != NULL); - while (g.CurrentTabBar != NULL) //-V1044 - { - if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name); - EndTabBar(); - } - while (window->DC.TreeDepth > 0) - { - if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); - TreePop(); - } - while (g.GroupStack.Size > window->DC.StackSizesOnBegin.SizeOfGroupStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); - EndGroup(); - } - while (window->IDStack.Size > 1) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); - PopID(); - } - while (g.ColorStack.Size > window->DC.StackSizesOnBegin.SizeOfColorStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); - PopStyleColor(); - } - while (g.StyleVarStack.Size > window->DC.StackSizesOnBegin.SizeOfStyleVarStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); - PopStyleVar(); - } - while (g.FocusScopeStack.Size > window->DC.StackSizesOnBegin.SizeOfFocusScopeStack) - { - if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); - PopFocusScope(); - } if (g.CurrentWindowStack.Size == 1) { - IM_ASSERT(g.CurrentWindow->IsFallbackWindow); + IM_ASSERT(window->IsFallbackWindow); break; } IM_ASSERT(window == g.CurrentWindow); @@ -7261,6 +7580,66 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi } } +// Must be called before End()/EndChild() +void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data) +{ + ImGuiContext& g = *GImGui; + while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) + { + if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name); + EndTable(); + } + + ImGuiWindow* window = g.CurrentWindow; + ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin; + IM_ASSERT(window != NULL); + while (g.CurrentTabBar != NULL) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name); + EndTabBar(); + } + while (window->DC.TreeDepth > 0) + { + if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); + TreePop(); + } + while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); + EndGroup(); + } + while (window->IDStack.Size > 1) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); + PopID(); + } + while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); + EndDisabled(); + } + while (g.ColorStack.Size > stack_sizes->SizeOfColorStack) + { + if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); + PopStyleColor(); + } + while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); + PopItemFlag(); + } + while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); + PopStyleVar(); + } + while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); + PopFocusScope(); + } +} + // Save current stack sizes for later compare void ImGuiStackSizes::SetToCurrentState() { @@ -7272,7 +7651,9 @@ void ImGuiStackSizes::SetToCurrentState() SizeOfFontStack = (short)g.FontStack.Size; SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size; SizeOfGroupStack = (short)g.GroupStack.Size; + SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size; SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size; + SizeOfDisabledStack = (short)g.DisabledStackSize; } // Compare to detect usage errors @@ -7290,6 +7671,8 @@ void ImGuiStackSizes::CompareWithCurrentState() // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them. IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!"); IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!"); + IM_ASSERT(SizeOfDisabledStack == g.DisabledStackSize && "BeginDisabled/EndDisabled Mismatch!"); + IM_ASSERT(SizeOfItemFlagsStack >= g.ItemFlagsStack.Size && "PushItemFlag/PopItemFlag Mismatch!"); IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!"); IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!"); IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!"); @@ -7324,7 +7707,6 @@ void ImGuiStackSizes::CompareWithCurrentState() // - GetContentRegionMaxAbs() [Internal] // - GetContentRegionAvail(), // - GetWindowContentRegionMin(), GetWindowContentRegionMax() -// - GetWindowContentRegionWidth() // - BeginGroup() // - EndGroup() // Also see in imgui_widgets: tab bars, columns. @@ -7374,14 +7756,23 @@ void ImGui::ItemSize(const ImRect& bb, float text_baseline_y) // Declare item bounding box for clipping and interaction. // Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface // declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction. -bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) +bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + // Set item data + // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set) + g.LastItemData.ID = id; + g.LastItemData.Rect = bb; + g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb; + g.LastItemData.InFlags = g.CurrentItemFlags | extra_flags; + g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None; + + // Directional navigation processing if (id != 0) { - // Navigation processing runs prior to clipping early-out + // Runs prior to clipping early-out // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of @@ -7390,11 +7781,16 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick). // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null. // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. - window->DC.NavLayerActiveMaskNext |= (1 << window->DC.NavLayerCurrent); + window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent); if (g.NavId == id || g.NavAnyRequest) if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) - NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); + NavProcessItem(); + + // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something". + // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something". + // READ THE FAQ: https://dearimgui.org/faq + IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!"); // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() #ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX @@ -7405,11 +7801,6 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) } #endif } - - // Equivalent to calling SetLastItemData() - window->DC.LastItemId = id; - window->DC.LastItemRect = bb; - window->DC.LastItemStatusFlags = ImGuiItemStatusFlags_None; g.NextItemData.Flags = ImGuiNextItemDataFlags_None; #ifdef IMGUI_ENABLE_TEST_ENGINE @@ -7418,14 +7809,14 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) #endif // Clipping test - const bool is_clipped = IsClippedEx(bb, id, false); + const bool is_clipped = IsClippedEx(bb, id); if (is_clipped) return false; //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) if (IsMouseHoveringRect(bb.Min, bb.Max)) - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; return true; } @@ -7685,14 +8076,9 @@ ImVec2 ImGui::GetWindowContentRegionMax() return window->ContentRegionRect.Max - window->Pos; } -float ImGui::GetWindowContentRegionWidth() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->ContentRegionRect.GetWidth(); -} - // Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) // Groups are currently a mishmash of functionalities which should perhaps be clarified and separated. +// FIXME-OPT: Could we safely early out on ->SkipItems? void ImGui::BeginGroup() { ImGuiContext& g = *GImGui; @@ -7748,7 +8134,7 @@ void ImGui::EndGroup() window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. ItemSize(group_bb.GetSize()); - ItemAdd(group_bb, 0); + ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop); // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group. // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets. @@ -7757,24 +8143,24 @@ void ImGui::EndGroup() const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId; const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true); if (group_contains_curr_active_id) - window->DC.LastItemId = g.ActiveId; + g.LastItemData.ID = g.ActiveId; else if (group_contains_prev_active_id) - window->DC.LastItemId = g.ActiveIdPreviousFrame; - window->DC.LastItemRect = group_bb; + g.LastItemData.ID = g.ActiveIdPreviousFrame; + g.LastItemData.Rect = group_bb; // Forward Hovered flag const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0; if (group_contains_curr_hovered_id) - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; // Forward Edited flag if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame) - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Edited; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; // Forward Deactivated flag - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDeactivated; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated; if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame) - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_Deactivated; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated; g.GroupStack.pop_back(); //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] @@ -7837,32 +8223,80 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) return scroll; } +void ImGui::ScrollToItem(ImGuiScrollFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ScrollToRectEx(window, g.LastItemData.NavRect, flags); +} + +void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags) +{ + ScrollToRectEx(window, item_rect, flags); +} + // Scroll to keep newly navigated item fully into view -ImVec2 ImGui::ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& item_rect) +ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags) { ImGuiContext& g = *GImGui; ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)); //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] - ImVec2 delta_scroll; - if (!window_rect.Contains(item_rect)) - { - if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x) - SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x - g.Style.ItemSpacing.x, 0.0f); - else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x) - SetScrollFromPosX(window, item_rect.Max.x - window->Pos.x + g.Style.ItemSpacing.x, 1.0f); - if (item_rect.Min.y < window_rect.Min.y) - SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y - g.Style.ItemSpacing.y, 0.0f); - else if (item_rect.Max.y >= window_rect.Max.y) - SetScrollFromPosY(window, item_rect.Max.y - window->Pos.y + g.Style.ItemSpacing.y, 1.0f); + // Check that only one behavior is selected per axis + IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_)); + IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_)); - ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); - delta_scroll = next_scroll - window->Scroll; + // Defaults + ImGuiScrollFlags in_flags = flags; + if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX) + flags |= ImGuiScrollFlags_KeepVisibleEdgeX; + if ((flags & ImGuiScrollFlags_MaskY_) == 0) + flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY; + + const bool fully_visible_x = item_rect.Min.x >= window_rect.Min.x && item_rect.Max.x <= window_rect.Max.x; + const bool fully_visible_y = item_rect.Min.y >= window_rect.Min.y && item_rect.Max.y <= window_rect.Max.y; + const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= window_rect.GetWidth(); + const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= window_rect.GetHeight(); + + if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x) + { + if (item_rect.Min.x < window_rect.Min.x || !can_be_fully_visible_x) + SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f); + else if (item_rect.Max.x >= window_rect.Max.x) + SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f); + } + else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX)) + { + float target_x = can_be_fully_visible_x ? ImFloor((item_rect.Min.x + item_rect.Max.x - window->InnerRect.GetWidth()) * 0.5f) : item_rect.Min.x; + SetScrollFromPosX(window, target_x - window->Pos.x, 0.0f); } + if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y) + { + if (item_rect.Min.y < window_rect.Min.y || !can_be_fully_visible_y) + SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f); + else if (item_rect.Max.y >= window_rect.Max.y) + SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f); + } + else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY)) + { + float target_y = can_be_fully_visible_y ? ImFloor((item_rect.Min.y + item_rect.Max.y - window->InnerRect.GetHeight()) * 0.5f) : item_rect.Min.y; + SetScrollFromPosY(window, target_y - window->Pos.y, 0.0f); + } + + ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); + ImVec2 delta_scroll = next_scroll - window->Scroll; + // Also scroll parent window to keep us into view if necessary - if (window->Flags & ImGuiWindowFlags_ChildWindow) - delta_scroll += ScrollToBringRectIntoView(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll)); + if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow)) + { + // FIXME-SCROLL: May be an option? + if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0) + in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX; + if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0) + in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY; + delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags); + } return delta_scroll; } @@ -7963,7 +8397,7 @@ void ImGui::SetScrollHereX(float center_x_ratio) ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x); - float target_pos_x = ImLerp(window->DC.LastItemRect.Min.x - spacing_x, window->DC.LastItemRect.Max.x + spacing_x, center_x_ratio); + float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio); SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos // Tweak: snap on edges when aiming at an item very close to the edge @@ -7989,10 +8423,10 @@ void ImGui::SetScrollHereY(float center_y_ratio) void ImGui::BeginTooltip() { - BeginTooltipEx(ImGuiWindowFlags_None, ImGuiTooltipFlags_None); + BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None); } -void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags tooltip_flags) +void ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags) { ImGuiContext& g = *GImGui; @@ -8021,7 +8455,7 @@ void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags toolt ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); } ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize; - Begin(window_name, NULL, flags | extra_flags); + Begin(window_name, NULL, flags | extra_window_flags); } void ImGui::EndTooltip() @@ -8032,7 +8466,7 @@ void ImGui::EndTooltip() void ImGui::SetTooltipV(const char* fmt, va_list args) { - BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip); + BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None); TextV(fmt, args); EndTooltip(); } @@ -8100,6 +8534,16 @@ ImGuiWindow* ImGui::GetTopMostPopupModal() return NULL; } +ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal() +{ + ImGuiContext& g = *GImGui; + for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--) + if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) + if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup)) + return popup; + return NULL; +} + void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; @@ -8192,7 +8636,7 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to bool ref_window_is_descendent_of_popup = false; for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++) if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window) - if (popup_window->RootWindow == ref_window->RootWindow) + if (IsWindowWithinBeginStackOf(ref_window, popup_window)) { ref_window_is_descendent_of_popup = true; break; @@ -8208,6 +8652,21 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to } } +void ImGui::ClosePopupsExceptModals() +{ + ImGuiContext& g = *GImGui; + + int popup_count_to_keep; + for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--) + { + ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window; + if (!window || window->Flags & ImGuiWindowFlags_Modal) + break; + } + if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below + ClosePopupToLevel(popup_count_to_keep, true); +} + void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) { ImGuiContext& g = *GImGui; @@ -8250,7 +8709,7 @@ void ImGui::CloseCurrentPopup() ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window; bool close_parent = false; if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu)) - if (parent_popup_window == NULL || !(parent_popup_window->Flags & ImGuiWindowFlags_Modal)) + if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar)) close_parent = true; if (!close_parent) break; @@ -8278,7 +8737,7 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) char name[20]; if (flags & ImGuiWindowFlags_ChildMenu) - ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth + ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuCount); // Recycle windows based on depth else ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame @@ -8343,7 +8802,7 @@ void ImGui::EndPopup() IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls IM_ASSERT(g.BeginPopupStack.Size > 0); - // Make all menus and popups wrap around for now, may need to expose that policy. + // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests) if (g.NavWindow == window) NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY); @@ -8359,12 +8818,13 @@ void ImGui::EndPopup() // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup() void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags) { - ImGuiWindow* window = GImGui->CurrentWindow; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) { - ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) + ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! + IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) OpenPopupEx(id, popup_flags); } } @@ -8387,11 +8847,12 @@ void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags // The main difference being that this is tweaked to avoid computing the ID twice. bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags) { - ImGuiWindow* window = GImGui->CurrentWindow; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return false; - ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) + ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! + IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_); if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) OpenPopupEx(id, popup_flags); @@ -8400,7 +8861,8 @@ bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flag bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags) { - ImGuiWindow* window = GImGui->CurrentWindow; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; if (!str_id) str_id = "window_context"; ImGuiID id = window->GetID(str_id); @@ -8413,7 +8875,8 @@ bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_fl bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags) { - ImGuiWindow* window = GImGui->CurrentWindow; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; if (!str_id) str_id = "void_context"; ImGuiID id = window->GetID(str_id); @@ -8504,7 +8967,7 @@ ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& s } // Note that this is used for popups, which can overlap the non work-area of individual viewports. -ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) +ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window) { ImGuiContext& g = *GImGui; IM_UNUSED(window); @@ -8518,13 +8981,13 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - ImRect r_outer = GetWindowAllowedExtentRect(window); + ImRect r_outer = GetPopupAllowedExtentRect(window); if (window->Flags & ImGuiWindowFlags_ChildMenu) { // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds. // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. IM_ASSERT(g.CurrentWindow == window); - ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; + ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2].Window; float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). ImRect r_avoid; if (parent_window->DC.MenuBarAppending) @@ -8559,18 +9022,17 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) //----------------------------------------------------------------------------- // FIXME-NAV: The existence of SetNavID vs SetFocusID properly needs to be clarified/reworked. -void ImGui::SetNavID(ImGuiID id, int nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) +// In our terminology those should be interchangeable. Those two functions are merely a legacy artifact, so at minimum naming should be clarified. +void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) { ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow != NULL); IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu); g.NavId = id; - g.NavLayer = (ImGuiNavLayer)nav_layer; + g.NavLayer = nav_layer; g.NavFocusScopeId = focus_scope_id; g.NavWindow->NavLastIds[nav_layer] = id; g.NavWindow->NavRectRel[nav_layer] = rect_rel; - //g.NavDisableHighlight = false; - //g.NavDisableMouseHover = g.NavMousePosDirty = true; } void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) @@ -8588,8 +9050,8 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) g.NavLayer = nav_layer; g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; window->NavLastIds[nav_layer] = id; - if (window->DC.LastItemId == id) - window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos); + if (g.LastItemData.ID == id) + window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect); if (g.ActiveIdSource == ImGuiInputSource_Nav) g.NavDisableMouseHover = true; @@ -8620,7 +9082,7 @@ static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y); r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y); } - else + else // FIXME: PageUp/PageDown are leaving move_dir == None { r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x); r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x); @@ -8628,15 +9090,17 @@ static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect } // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057 -static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) +static bool ImGui::NavScoreItem(ImGuiNavItemData* result) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (g.NavLayer != window->DC.NavLayerCurrent) return false; - const ImRect& curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) - g.NavScoringCount++; + // FIXME: Those are not good variables names + ImRect cand = g.LastItemData.NavRect; // Current item nav rectangle + const ImRect curr = g.NavScoringRect; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) + g.NavScoringDebugCount++; // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring if (window->ParentWindow == g.NavWindow) @@ -8686,7 +9150,7 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) else { // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter) - quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; + quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; } #if IMGUI_DEBUG_NAV_SCORING @@ -8698,24 +9162,24 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150)); - draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); + draw_list->AddText(cand.Max, ~0U, buf); } else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. { - if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } if (quadrant == g.NavMoveDir) { ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); ImDrawList* draw_list = GetForegroundDrawList(window); draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); - draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); + draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf); } } #endif // Is it in the quadrant we're interesting in moving to? bool new_best = false; - if (quadrant == g.NavMoveDir) + const ImGuiDir move_dir = g.NavMoveDir; + if (quadrant == move_dir) { // Does it beat the current best candidate? if (dist_box < result->DistBox) @@ -8737,7 +9201,7 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. - if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance + if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance new_best = true; } } @@ -8750,7 +9214,7 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) - if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) + if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f)) { result->DistAxial = dist_axial; new_best = true; @@ -8759,34 +9223,38 @@ static bool ImGui::NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) return new_best; } -static void ImGui::NavApplyItemToResult(ImGuiNavMoveResult* result, ImGuiWindow* window, ImGuiID id, const ImRect& nav_bb_rel) +static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result) { + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; result->Window = window; - result->ID = id; + result->ID = g.LastItemData.ID; result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; - result->RectRel = nav_bb_rel; + result->InFlags = g.LastItemData.InFlags; + result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect); } // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) -static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) +// This is called after LastItemData is set. +static void ImGui::NavProcessItem() { ImGuiContext& g = *GImGui; - //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. - // return; - - const ImGuiItemFlags item_flags = window->DC.ItemFlags; - const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); + ImGuiWindow* window = g.CurrentWindow; + const ImGuiID id = g.LastItemData.ID; + const ImRect nav_bb = g.LastItemData.NavRect; + const ImGuiItemFlags item_flags = g.LastItemData.InFlags; // Process Init Request if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) { // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback - if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0) + const bool candidate_for_nav_default_focus = (item_flags & (ImGuiItemFlags_NoNavDefaultFocus | ImGuiItemFlags_Disabled)) == 0; + if (candidate_for_nav_default_focus || g.NavInitResultId == 0) { g.NavInitResultId = id; - g.NavInitResultRectRel = nav_bb_rel; + g.NavInitResultRectRel = WindowRectAbsToRel(window, nav_bb); } - if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) + if (candidate_for_nav_default_focus) { g.NavInitRequest = false; // Found a match, clear request NavUpdateAnyRequestFlag(); @@ -8794,27 +9262,32 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con } // Process Move Request (scoring for navigation) - // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) - if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) + // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy) + if (g.NavMoveScoringItems) { - ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; -#if IMGUI_DEBUG_NAV_SCORING - // [DEBUG] Score all items in NavWindow at all times - if (!g.NavMoveRequest) - g.NavMoveDir = g.NavMoveDirLast; - bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest; -#else - bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb); -#endif - if (new_best) - NavApplyItemToResult(result, window, id, nav_bb_rel); + const bool is_tab_stop = (item_flags & ImGuiItemFlags_Inputable) && (item_flags & (ImGuiItemFlags_NoTabStop | ImGuiItemFlags_Disabled)) == 0; + const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) != 0; + if (is_tabbing) + { + if (is_tab_stop || (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi)) + NavProcessItemForTabbingRequest(id); + } + else if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) + { + ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + if (!is_tabbing) + { + if (NavScoreItem(result)) + NavApplyItemToResult(result); - // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. - const float VISIBLE_RATIO = 0.70f; - if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) - if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) - if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb)) - NavApplyItemToResult(&g.NavMoveResultLocalVisibleSet, window, id, nav_bb_rel); + // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. + const float VISIBLE_RATIO = 0.70f; + if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) + if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) + if (NavScoreItem(&g.NavMoveResultLocalVisible)) + NavApplyItemToResult(&g.NavMoveResultLocalVisible); + } + } } // Update window-relative bounding box of navigated item @@ -8824,44 +9297,124 @@ static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, con g.NavLayer = window->DC.NavLayerCurrent; g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; g.NavIdIsAlive = true; - g.NavIdTabCounter = window->DC.FocusCounterTabStop; - window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) + window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position) + } +} + +// Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest(). +// Note that SetKeyboardFocusHere() API calls are considered tabbing requests! +// - Case 1: no nav/active id: set result to first eligible item, stop storing. +// - Case 2: tab forward: on ref id set counter, on counter elapse store result +// - Case 3: tab forward wrap: set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. // FIXME-TABBING: Could be done as a next-frame forwarded request +// - Case 4: tab backward: store all results, on ref id pick prev, stop storing +// - Case 5: tab backward wrap: store all results, on ref id if no result keep storing until last // FIXME-TABBING: Could be done as next-frame forwarded requested +void ImGui::NavProcessItemForTabbingRequest(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + + // Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows) + ImGuiNavItemData* result = &g.NavMoveResultLocal; + if (g.NavTabbingDir == +1) + { + // Tab Forward or SetKeyboardFocusHere() with >= 0 + if (g.NavTabbingResultFirst.ID == 0) + NavApplyItemToResult(&g.NavTabbingResultFirst); + if (--g.NavTabbingCounter == 0) + NavMoveRequestResolveWithLastItem(result); + else if (g.NavId == id) + g.NavTabbingCounter = 1; + } + else if (g.NavTabbingDir == -1) + { + // Tab Backward + if (g.NavId == id) + { + if (result->ID) + { + g.NavMoveScoringItems = false; + NavUpdateAnyRequestFlag(); + } + } + else + { + NavApplyItemToResult(result); + } + } + else if (g.NavTabbingDir == 0) + { + // Tab Init + if (g.NavTabbingResultFirst.ID == 0) + NavMoveRequestResolveWithLastItem(&g.NavTabbingResultFirst); } } bool ImGui::NavMoveRequestButNoResultYet() { ImGuiContext& g = *GImGui; - return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; + return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; +} + +// FIXME: ScoringRect is not set +void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.NavWindow != NULL); + + if (move_flags & ImGuiNavMoveFlags_Tabbing) + move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId; + + g.NavMoveSubmitted = g.NavMoveScoringItems = true; + g.NavMoveDir = move_dir; + g.NavMoveDirForDebug = move_dir; + g.NavMoveClipDir = clip_dir; + g.NavMoveFlags = move_flags; + g.NavMoveScrollFlags = scroll_flags; + g.NavMoveForwardToNextFrame = false; + g.NavMoveKeyMods = g.IO.KeyMods; + g.NavTabbingCounter = 0; + g.NavMoveResultLocal.Clear(); + g.NavMoveResultLocalVisible.Clear(); + g.NavMoveResultOther.Clear(); + NavUpdateAnyRequestFlag(); +} + +void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result) +{ + ImGuiContext& g = *GImGui; + g.NavMoveScoringItems = false; // Ensure request doesn't need more processing + NavApplyItemToResult(result); + NavUpdateAnyRequestFlag(); } void ImGui::NavMoveRequestCancel() { ImGuiContext& g = *GImGui; - g.NavMoveRequest = false; + g.NavMoveSubmitted = g.NavMoveScoringItems = false; NavUpdateAnyRequestFlag(); } -void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags) +// Forward will reuse the move request again on the next frame (generally with modifications done to it) +void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags) { ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None); + IM_ASSERT(g.NavMoveForwardToNextFrame == false); NavMoveRequestCancel(); + g.NavMoveForwardToNextFrame = true; g.NavMoveDir = move_dir; g.NavMoveClipDir = clip_dir; - g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; - g.NavMoveRequestFlags = move_flags; - g.NavWindow->NavRectRel[g.NavLayer] = bb_rel; + g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded; + g.NavMoveScrollFlags = scroll_flags; } -void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags) +// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire +// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final. +void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags) { ImGuiContext& g = *GImGui; - - // Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire - // popup is assembled and in case of appended popups it is not clear which EndPopup() call is final. - g.NavWrapRequestWindow = window; - g.NavWrapRequestFlags = move_flags; + IM_ASSERT(wrap_flags != 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY + // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it, NavEndFrame() will do the same test + if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main) + g.NavMoveFlags |= wrap_flags; } // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0). @@ -8893,8 +9446,6 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer) if (window->NavLastIds[layer] != 0) { SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); - g.NavDisableHighlight = false; - g.NavDisableMouseHover = g.NavMousePosDirty = true; } else { @@ -8903,10 +9454,17 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer) } } +void ImGui::NavRestoreHighlightAfterMove() +{ + ImGuiContext& g = *GImGui; + g.NavDisableHighlight = false; + g.NavDisableMouseHover = g.NavMousePosDirty = true; +} + static inline void ImGui::NavUpdateAnyRequestFlag() { ImGuiContext& g = *GImGui; - g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); + g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); if (g.NavAnyRequest) IM_ASSERT(g.NavWindow != NULL); } @@ -8916,10 +9474,16 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) { ImGuiContext& g = *GImGui; IM_ASSERT(window == g.NavWindow); + + if (window->Flags & ImGuiWindowFlags_NoNavInputs) + { + g.NavId = g.NavFocusScopeId = 0; + return; + } + bool init_for_nav = false; - if (!(window->Flags & ImGuiWindowFlags_NoNavInputs)) - if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) - init_for_nav = true; + if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) + init_for_nav = true; IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer); if (init_for_nav) { @@ -8940,18 +9504,25 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) static ImVec2 ImGui::NavCalcPreferredRefPos() { ImGuiContext& g = *GImGui; - if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) + ImGuiWindow* window = g.NavWindow; + if (g.NavDisableHighlight || !g.NavDisableMouseHover || !window) { // Mouse (we need a fallback in case the mouse becomes invalid after being used) if (IsMousePosValid(&g.IO.MousePos)) return g.IO.MousePos; - return g.LastValidMousePos; + return g.MouseLastValidPos; } else { - // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item. - const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; - ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); + // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item + // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?) + ImRect rect_rel = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]); + if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX)) + { + ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); + rect_rel.Translate(window->Scroll - next_scroll); + } + ImVec2 pos = ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); ImGuiViewport* viewport = GetMainViewport(); return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. } @@ -8982,6 +9553,8 @@ float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) { ImVec2 delta(0.0f, 0.0f); + if (dir_sources & ImGuiNavDirSourceFlags_RawKeyboard) + delta += ImVec2((float)IsKeyDown(GetKeyIndex(ImGuiKey_RightArrow)) - (float)IsKeyDown(GetKeyIndex(ImGuiKey_LeftArrow)), (float)IsKeyDown(GetKeyIndex(ImGuiKey_DownArrow)) - (float)IsKeyDown(GetKeyIndex(ImGuiKey_UpArrow))); if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) @@ -9001,16 +9574,12 @@ static void ImGui::NavUpdate() ImGuiIO& io = g.IO; io.WantSetMousePos = false; - g.NavWrapRequestWindow = NULL; - g.NavWrapRequestFlags = ImGuiNavMoveFlags_None; -#if 0 - if (g.NavScoringCount > 0) IMGUI_DEBUG_LOG("NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); -#endif + //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG("NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) // (do it before we map Keyboard input!) - bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad) { if (io.NavInputs[ImGuiNavInput_Activate] > 0.0f || io.NavInputs[ImGuiNavInput_Input] > 0.0f || io.NavInputs[ImGuiNavInput_Cancel] > 0.0f || io.NavInputs[ImGuiNavInput_Menu] > 0.0f @@ -9033,8 +9602,6 @@ static void ImGui::NavUpdate() io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; if (io.KeyShift) io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; - if (io.KeyAlt && !io.KeyCtrl) // AltGR is Alt+Ctrl, also even on keyboards without AltGR we don't want Alt+Ctrl to open menu. - io.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; #undef NAV_MAP_KEY } memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration)); @@ -9043,42 +9610,27 @@ static void ImGui::NavUpdate() // Process navigation init request (select first/default focus) if (g.NavInitResultId != 0) - NavUpdateInitResult(); + NavInitRequestApplyResult(); g.NavInitRequest = false; g.NavInitRequestFromMove = false; g.NavInitResultId = 0; g.NavJustMovedToId = 0; // Process navigation move request - if (g.NavMoveRequest) - NavUpdateMoveResult(); + if (g.NavMoveSubmitted) + NavMoveRequestApplyResult(); + g.NavTabbingCounter = 0; + g.NavMoveSubmitted = g.NavMoveScoringItems = false; - // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame - if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) - { - IM_ASSERT(g.NavMoveRequest); - if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) - g.NavDisableHighlight = false; - g.NavMoveRequestForward = ImGuiNavForward_None; - } - - // Apply application mouse position movement, after we had a chance to process move request result. + // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling) + bool set_mouse_pos = false; if (g.NavMousePosDirty && g.NavIdIsAlive) - { - // Set mouse position given our knowledge of the navigated item position from last frame - if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) - if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) - { - io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); - io.WantSetMousePos = true; - } - g.NavMousePosDirty = false; - } - g.NavIdIsAlive = false; - g.NavJustTabbedId = 0; - IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); + if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) + set_mouse_pos = true; + g.NavMousePosDirty = false; + IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu); - // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 + // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0 if (g.NavWindow) NavSaveLastChildNavWindowIntoParent(g.NavWindow); if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main) @@ -9092,117 +9644,55 @@ static void ImGui::NavUpdate() io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL); // Process NavCancel input (to close a popup, get back to parent, clear focus) - if (IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) - { - IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n"); - if (g.ActiveId != 0) - { - if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) - ClearActiveID(); - } - else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) - { - // Exit child window - ImGuiWindow* child_window = g.NavWindow; - ImGuiWindow* parent_window = g.NavWindow->ParentWindow; - IM_ASSERT(child_window->ChildId != 0); - ImRect child_rect = child_window->Rect(); - FocusWindow(parent_window); - SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, ImRect(child_rect.Min - parent_window->Pos, child_rect.Max - parent_window->Pos)); - } - else if (g.OpenPopupStack.Size > 0) - { - // Close open popup/menu - if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) - ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); - } - else if (g.NavLayer != ImGuiNavLayer_Main) - { - // Leave the "menu" layer - NavRestoreLayer(ImGuiNavLayer_Main); - } - else - { - // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were - if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) - g.NavWindow->NavLastIds[0] = 0; - g.NavId = g.NavFocusScopeId = 0; - } - } + NavUpdateCancelRequest(); // Process manual activation request - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavActivateInputId = 0; + g.NavActivateFlags = ImGuiActivateFlags_None; if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); + bool input_down = IsNavInputDown(ImGuiNavInput_Input); bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); + bool input_pressed = input_down && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); if (g.ActiveId == 0 && activate_pressed) + { g.NavActivateId = g.NavId; + g.NavActivateFlags = ImGuiActivateFlags_PreferTweak; + } + if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed) + { + g.NavActivateInputId = g.NavId; + g.NavActivateFlags = ImGuiActivateFlags_PreferInput; + } if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) g.NavActivateDownId = g.NavId; if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) g.NavActivatePressedId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) - g.NavInputId = g.NavId; } if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) g.NavDisableHighlight = true; if (g.NavActivateId != 0) IM_ASSERT(g.NavActivateDownId == g.NavActivateId); - g.NavMoveRequest = false; // Process programmatic activation request + // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others) if (g.NavNextActivateId != 0) - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; + { + if (g.NavNextActivateFlags & ImGuiActivateFlags_PreferInput) + g.NavActivateInputId = g.NavNextActivateId; + else + g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId; + g.NavActivateFlags = g.NavNextActivateFlags; + } g.NavNextActivateId = 0; - // Initiate directional inputs request - if (g.NavMoveRequestForward == ImGuiNavForward_None) - { - g.NavMoveDir = ImGuiDir_None; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_None; - if (g.NavWindow && !g.NavWindowingTarget && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - { - const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat; - if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; } - } - g.NavMoveClipDir = g.NavMoveDir; - } - else - { - // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) - // (Preserve g.NavMoveRequestFlags, g.NavMoveClipDir which were set by the NavMoveRequestForward() function) - IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); - IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_ForwardQueued); - IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); - g.NavMoveRequestForward = ImGuiNavForward_ForwardActive; - } - - // Update PageUp/PageDown/Home/End scroll - // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? - float nav_scoring_rect_offset_y = 0.0f; - if (nav_keyboard_active) - nav_scoring_rect_offset_y = NavUpdatePageUpPageDown(); - - // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match - if (g.NavMoveDir != ImGuiDir_None) - { - g.NavMoveRequest = true; - g.NavMoveRequestKeyMods = io.KeyMods; - g.NavMoveDirLast = g.NavMoveDir; - } - if (g.NavMoveRequest && g.NavId == 0) - { - IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer); - g.NavInitRequest = g.NavInitRequestFromMove = true; - // Reassigning with same value, we're being explicit here. - g.NavInitResultId = 0; // -V1048 - g.NavDisableHighlight = false; - } + // Process move requests + NavUpdateCreateMoveRequest(); + if (g.NavMoveDir == ImGuiDir_None) + NavUpdateCreateTabbingRequest(); NavUpdateAnyRequestFlag(); + g.NavIdIsAlive = false; // Scrolling if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) @@ -9210,12 +9700,13 @@ static void ImGui::NavUpdate() // *Fallback* manual-scroll with Nav directional keys when window has no navigable item ImGuiWindow* window = g.NavWindow; const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. - if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) + const ImGuiDir move_dir = g.NavMoveDir; + if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && move_dir != ImGuiDir_None) { - if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) - SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); - if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) - SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); + if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) + SetScrollX(window, ImFloor(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); + if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) + SetScrollY(window, ImFloor(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); } // *Normal* Manual scroll with NavScrollXXX keys @@ -9227,18 +9718,125 @@ static void ImGui::NavUpdate() SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); } - // Reset search results - g.NavMoveResultLocal.Clear(); - g.NavMoveResultLocalVisibleSet.Clear(); - g.NavMoveResultOther.Clear(); + // Always prioritize mouse highlight if navigation is disabled + if (!nav_keyboard_active && !nav_gamepad_active) + { + g.NavDisableHighlight = true; + g.NavDisableMouseHover = set_mouse_pos = false; + } + + // Update mouse position if requested + // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied) + if (set_mouse_pos && (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) + { + io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); + io.WantSetMousePos = true; + //IMGUI_DEBUG_LOG("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); + } + + // [DEBUG] + g.NavScoringDebugCount = 0; +#if IMGUI_DEBUG_NAV_RECTS + if (g.NavWindow) + { + ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); + if (1) { for (int layer = 0; layer < 2; layer++) { ImRect r = WindowRectRelToAbs(g.NavWindow, g.NavWindow->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255,200,0,255)); } } // [DEBUG] + if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + } +#endif +} + +void ImGui::NavInitRequestApplyResult() +{ + // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void) + ImGuiContext& g = *GImGui; + if (!g.NavWindow) + return; + + // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) + // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently. + IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); + SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); + g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result + if (g.NavInitRequestFromMove) + NavRestoreHighlightAfterMove(); +} + +void ImGui::NavUpdateCreateMoveRequest() +{ + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + ImGuiWindow* window = g.NavWindow; + + if (g.NavMoveForwardToNextFrame && window != NULL) + { + // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window) + // (preserve most state, which were already set by the NavMoveRequestForward() function) + IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None); + IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded); + IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir); + } + else + { + // Initiate directional inputs request + g.NavMoveDir = ImGuiDir_None; + g.NavMoveFlags = ImGuiNavMoveFlags_None; + g.NavMoveScrollFlags = ImGuiScrollFlags_None; + if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs)) + { + const ImGuiInputReadMode read_mode = ImGuiInputReadMode_Repeat; + if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; } + } + g.NavMoveClipDir = g.NavMoveDir; + g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); + } + + // Update PageUp/PageDown/Home/End scroll + // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + float scoring_rect_offset_y = 0.0f; + if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active) + scoring_rect_offset_y = NavUpdatePageUpPageDown(); + if (scoring_rect_offset_y != 0.0f) + { + g.NavScoringNoClipRect = window->InnerRect; + g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y); + } + + // [DEBUG] Always send a request +#if IMGUI_DEBUG_NAV_SCORING + if (io.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) + g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3); + if (io.KeyCtrl && g.NavMoveDir == ImGuiDir_None) + { + g.NavMoveDir = g.NavMoveDirForDebug; + g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult; + } +#endif + + // Submit + g.NavMoveForwardToNextFrame = false; + if (g.NavMoveDir != ImGuiDir_None) + NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); + + // Moving with no reference triggers a init request (will be used as a fallback if the direction fails to find a match) + if (g.NavMoveSubmitted && g.NavId == 0) + { + IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", g.NavWindow->Name, g.NavLayer); + g.NavInitRequest = g.NavInitRequestFromMove = true; + g.NavInitResultId = 0; + g.NavDisableHighlight = false; + } // When using gamepad, we project the reference nav bounding box into window visible area. // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative // (can't focus a visible object like we can with the mouse). - if (g.NavMoveRequest && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main) + if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL) { - ImGuiWindow* window = g.NavWindow; - ImRect window_rect_rel(window->InnerRect.Min - window->Pos - ImVec2(1, 1), window->InnerRect.Max - window->Pos + ImVec2(1, 1)); + ImRect window_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1))); if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) { IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel\n"); @@ -9250,63 +9848,78 @@ static void ImGui::NavUpdate() } // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) - ImRect nav_rect_rel = g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted() ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); - g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0, 0, 0, 0); - g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y); - g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x); - g.NavScoringRect.Max.x = g.NavScoringRect.Min.x; - IM_ASSERT(!g.NavScoringRect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). - //GetForegroundDrawList()->AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] - g.NavScoringCount = 0; -#if IMGUI_DEBUG_NAV_RECTS - if (g.NavWindow) + ImRect scoring_rect; + if (window != NULL) { - ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); - if (1) { for (int layer = 0; layer < 2; layer++) draw_list->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] - if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); + scoring_rect = WindowRectRelToAbs(window, nav_rect_rel); + scoring_rect.TranslateY(scoring_rect_offset_y); + scoring_rect.Min.x = ImMin(scoring_rect.Min.x + 1.0f, scoring_rect.Max.x); + scoring_rect.Max.x = scoring_rect.Min.x; + IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). + //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG] + //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG] } + g.NavScoringRect = scoring_rect; + g.NavScoringNoClipRect.Add(scoring_rect); +} + +void ImGui::NavUpdateCreateTabbingRequest() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.NavWindow; + IM_ASSERT(g.NavMoveDir == ImGuiDir_None); + if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs)) + return; + + const bool tab_pressed = IsKeyPressedMap(ImGuiKey_Tab, true) && !IsActiveIdUsingKey(ImGuiKey_Tab) && !g.IO.KeyCtrl && !g.IO.KeyAlt; + if (!tab_pressed) + return; + + // Initiate tabbing request + // (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!) + // Initially this was designed to use counters and modulo arithmetic, but that could not work with unsubmitted items (list clipper). Instead we use a strategy close to other move requests. + // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping. + //// FIXME: We use (g.ActiveId == 0) but (g.NavDisableHighlight == false) might be righter once we can tab through anything + g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1; + ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; + ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down; + NavMoveRequestSubmit(ImGuiDir_None, clip_dir, ImGuiNavMoveFlags_Tabbing, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. + g.NavTabbingResultFirst.Clear(); + g.NavTabbingCounter = -1; +} + +// Apply result from previous frame navigation directional move request. Always called from NavUpdate() +void ImGui::NavMoveRequestApplyResult() +{ + ImGuiContext& g = *GImGui; +#if IMGUI_DEBUG_NAV_SCORING + if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times + return; #endif -} - -static void ImGui::NavUpdateInitResult() -{ - // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void) - ImGuiContext& g = *GImGui; - if (!g.NavWindow) - return; - - // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) - IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); - SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); - if (g.NavInitRequestFromMove) - { - g.NavDisableHighlight = false; - g.NavDisableMouseHover = g.NavMousePosDirty = true; - } -} - -// Apply result from previous frame navigation directional move request -static void ImGui::NavUpdateMoveResult() -{ - ImGuiContext& g = *GImGui; - if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) - { - // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) - if (g.NavId != 0) - { - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - } - return; - } // Select which result to use - ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL; + + // Tabbing forward wrap + if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) + if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID) + result = &g.NavTabbingResultFirst; + + // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) + if (result == NULL) + { + if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) + g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; + if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) + NavRestoreHighlightAfterMove(); + return; + } // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. - if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) - if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId) - result = &g.NavMoveResultLocalVisibleSet; + if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) + if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId) + result = &g.NavMoveResultLocalVisible; // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules. if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) @@ -9317,109 +9930,178 @@ static void ImGui::NavUpdateMoveResult() // Scroll to keep newly navigated item fully into view. if (g.NavLayer == ImGuiNavLayer_Main) { - ImVec2 delta_scroll; - if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_ScrollToEdge) + if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY) { + // FIXME: Should remove this float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; - delta_scroll.y = result->Window->Scroll.y - scroll_target; SetScrollY(result->Window, scroll_target); } else { - ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos); - delta_scroll = ScrollToBringRectIntoView(result->Window, rect_abs); + ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel); + ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags); } - - // Offset our result position so mouse position can be applied immediately after in NavUpdate() - result->RectRel.TranslateX(-delta_scroll.x); - result->RectRel.TranslateY(-delta_scroll.y); } - ClearActiveID(); g.NavWindow = result->Window; + if (g.ActiveId != result->ID) + ClearActiveID(); if (g.NavId != result->ID) { // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) g.NavJustMovedToId = result->ID; g.NavJustMovedToFocusScopeId = result->FocusScopeId; - g.NavJustMovedToKeyMods = g.NavMoveRequestKeyMods; + g.NavJustMovedToKeyMods = g.NavMoveKeyMods; } + + // Focus IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name); SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); - g.NavDisableHighlight = false; - g.NavDisableMouseHover = g.NavMousePosDirty = true; + + // Tabbing: Activates Inputable or Focus non-Inputable + if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && (result->InFlags & ImGuiItemFlags_Inputable)) + { + g.NavNextActivateId = result->ID; + g.NavNextActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState; + g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; + } + + // Activate + if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate) + { + g.NavNextActivateId = result->ID; + g.NavNextActivateFlags = ImGuiActivateFlags_None; + } + + // Enable nav highlight + if ((g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) + NavRestoreHighlightAfterMove(); +} + +// Process NavCancel input (to close a popup, get back to parent, clear focus) +// FIXME: In order to support e.g. Escape to clear a selection we'll need: +// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it. +// - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept +static void ImGui::NavUpdateCancelRequest() +{ + ImGuiContext& g = *GImGui; + if (!IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) + return; + + IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n"); + if (g.ActiveId != 0) + { + if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) + ClearActiveID(); + } + else if (g.NavLayer != ImGuiNavLayer_Main) + { + // Leave the "menu" layer + NavRestoreLayer(ImGuiNavLayer_Main); + NavRestoreHighlightAfterMove(); + } + else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) + { + // Exit child window + ImGuiWindow* child_window = g.NavWindow; + ImGuiWindow* parent_window = g.NavWindow->ParentWindow; + IM_ASSERT(child_window->ChildId != 0); + ImRect child_rect = child_window->Rect(); + FocusWindow(parent_window); + SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_rect)); + NavRestoreHighlightAfterMove(); + } + else if (g.OpenPopupStack.Size > 0) + { + // Close open popup/menu + if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) + ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); + } + else + { + // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were + if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) + g.NavWindow->NavLastIds[0] = 0; + g.NavId = g.NavFocusScopeId = 0; + } } // Handle PageUp/PageDown/Home/End keys +// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request +// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference +// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid? static float ImGui::NavUpdatePageUpPageDown() { ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; - if (g.NavMoveDir != ImGuiDir_None || g.NavWindow == NULL) - return 0.0f; - if ((g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL || g.NavLayer != ImGuiNavLayer_Main) + ImGuiWindow* window = g.NavWindow; + if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL) return 0.0f; - ImGuiWindow* window = g.NavWindow; const bool page_up_held = IsKeyDown(io.KeyMap[ImGuiKey_PageUp]) && !IsActiveIdUsingKey(ImGuiKey_PageUp); const bool page_down_held = IsKeyDown(io.KeyMap[ImGuiKey_PageDown]) && !IsActiveIdUsingKey(ImGuiKey_PageDown); const bool home_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_Home]) && !IsActiveIdUsingKey(ImGuiKey_Home); const bool end_pressed = IsKeyPressed(io.KeyMap[ImGuiKey_End]) && !IsActiveIdUsingKey(ImGuiKey_End); - if (page_up_held != page_down_held || home_pressed != end_pressed) // If either (not both) are pressed + if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out + return 0.0f; + + if (g.NavLayer != ImGuiNavLayer_Main) + NavRestoreLayer(ImGuiNavLayer_Main); + + if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll) { - if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll) + // Fallback manual-scroll when window has no navigable item + if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) + SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); + else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) + SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); + else if (home_pressed) + SetScrollY(window, 0.0f); + else if (end_pressed) + SetScrollY(window, window->ScrollMax.y); + } + else + { + ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; + const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); + float nav_scoring_rect_offset_y = 0.0f; + if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) { - // Fallback manual-scroll when window has no navigable item - if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) - SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); - else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) - SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); - else if (home_pressed) - SetScrollY(window, 0.0f); - else if (end_pressed) - SetScrollY(window, window->ScrollMax.y); + nav_scoring_rect_offset_y = -page_offset_y; + g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item) + g.NavMoveClipDir = ImGuiDir_Up; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; } - else + else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) { - ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; - const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); - float nav_scoring_rect_offset_y = 0.0f; - if (IsKeyPressed(io.KeyMap[ImGuiKey_PageUp], true)) - { - nav_scoring_rect_offset_y = -page_offset_y; - g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item) - g.NavMoveClipDir = ImGuiDir_Up; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; - } - else if (IsKeyPressed(io.KeyMap[ImGuiKey_PageDown], true)) - { - nav_scoring_rect_offset_y = +page_offset_y; - g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item) - g.NavMoveClipDir = ImGuiDir_Down; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; - } - else if (home_pressed) - { - // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y - // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdge flag, we don't scroll immediately to avoid scrolling happening before nav result. - // Preserve current horizontal position if we have any. - nav_rect_rel.Min.y = nav_rect_rel.Max.y = -window->Scroll.y; - if (nav_rect_rel.IsInverted()) - nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; - g.NavMoveDir = ImGuiDir_Down; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; - } - else if (end_pressed) - { - nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ScrollMax.y + window->SizeFull.y - window->Scroll.y; - if (nav_rect_rel.IsInverted()) - nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; - g.NavMoveDir = ImGuiDir_Up; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdge; - } - return nav_scoring_rect_offset_y; + nav_scoring_rect_offset_y = +page_offset_y; + g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item) + g.NavMoveClipDir = ImGuiDir_Down; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; } + else if (home_pressed) + { + // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y + // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result. + // Preserve current horizontal position if we have any. + nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f; + if (nav_rect_rel.IsInverted()) + nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; + g.NavMoveDir = ImGuiDir_Down; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY; + // FIXME-NAV: MoveClipDir left to _None, intentional? + } + else if (end_pressed) + { + nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y; + if (nav_rect_rel.IsInverted()) + nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f; + g.NavMoveDir = ImGuiDir_Up; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY; + // FIXME-NAV: MoveClipDir left to _None, intentional? + } + return nav_scoring_rect_offset_y; } return 0.0f; } @@ -9433,63 +10115,74 @@ static void ImGui::NavEndFrame() NavUpdateWindowingOverlay(); // Perform wrap-around in menus - ImGuiWindow* window = g.NavWrapRequestWindow; - ImGuiNavMoveFlags move_flags = g.NavWrapRequestFlags; - if (window != NULL && g.NavWindow == window && NavMoveRequestButNoResultYet() && g.NavMoveRequestForward == ImGuiNavForward_None && g.NavLayer == ImGuiNavLayer_Main) - { - IM_ASSERT(move_flags != 0); // No points calling this with no wrapping - ImRect bb_rel = window->NavRectRel[0]; + // FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly. + // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame. + const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY; + if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) + NavUpdateCreateWrappingRequest(); +} - ImGuiDir clip_dir = g.NavMoveDir; - if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) +static void ImGui::NavUpdateCreateWrappingRequest() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.NavWindow; + + bool do_forward = false; + ImRect bb_rel = window->NavRectRel[g.NavLayer]; + ImGuiDir clip_dir = g.NavMoveDir; + const ImGuiNavMoveFlags move_flags = g.NavMoveFlags; + if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + { + bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x; + if (move_flags & ImGuiNavMoveFlags_WrapX) { - bb_rel.Min.x = bb_rel.Max.x = - ImMax(window->SizeFull.x, window->ContentSize.x + window->WindowPadding.x * 2.0f) - window->Scroll.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) - { - bb_rel.TranslateY(-bb_rel.GetHeight()); - clip_dir = ImGuiDir_Up; - } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); - } - if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) - { - bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) - { - bb_rel.TranslateY(+bb_rel.GetHeight()); - clip_dir = ImGuiDir_Down; - } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); - } - if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) - { - bb_rel.Min.y = bb_rel.Max.y = - ImMax(window->SizeFull.y, window->ContentSize.y + window->WindowPadding.y * 2.0f) - window->Scroll.y; - if (move_flags & ImGuiNavMoveFlags_WrapY) - { - bb_rel.TranslateX(-bb_rel.GetWidth()); - clip_dir = ImGuiDir_Left; - } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); - } - if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) - { - bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y; - if (move_flags & ImGuiNavMoveFlags_WrapY) - { - bb_rel.TranslateX(+bb_rel.GetWidth()); - clip_dir = ImGuiDir_Right; - } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); + bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row + clip_dir = ImGuiDir_Up; } + do_forward = true; } + if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) + { + bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x; + if (move_flags & ImGuiNavMoveFlags_WrapX) + { + bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row + clip_dir = ImGuiDir_Down; + } + do_forward = true; + } + if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + { + bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y; + if (move_flags & ImGuiNavMoveFlags_WrapY) + { + bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column + clip_dir = ImGuiDir_Left; + } + do_forward = true; + } + if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) + { + bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y; + if (move_flags & ImGuiNavMoveFlags_WrapY) + { + bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column + clip_dir = ImGuiDir_Right; + } + do_forward = true; + } + if (!do_forward) + return; + window->NavRectRel[g.NavLayer] = bb_rel; + NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags); } static int ImGui::FindWindowFocusIndex(ImGuiWindow* window) { ImGuiContext& g = *GImGui; + IM_UNUSED(g); int order = window->FocusOrder; + IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking) IM_ASSERT(g.WindowsFocusOrder[order] == window); return order; } @@ -9525,6 +10218,8 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir) static void ImGui::NavUpdateWindowing() { ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + ImGuiWindow* apply_focus_window = NULL; bool apply_toggle_layer = false; @@ -9536,25 +10231,25 @@ static void ImGui::NavUpdateWindowing() // Fade out if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL) { - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - g.IO.DeltaTime * 10.0f, 0.0f); + g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f); if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f) g.NavWindowingTargetAnim = NULL; } - // Start CTRL-TAB or Square+L/R window selection - bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); + // Start CTRL+Tab or Square+L/R window selection + const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); + const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab); if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { - g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // FIXME-DOCK: Will need to use RootWindowDockStop + g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; - g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; + g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad; } // Gamepad update - g.NavWindowingTimer += g.IO.DeltaTime; + g.NavWindowingTimer += io.DeltaTime; if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad) { // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise @@ -9586,31 +10281,50 @@ static void ImGui::NavUpdateWindowing() // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f if (IsKeyPressedMap(ImGuiKey_Tab, true)) - NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1); - if (!g.IO.KeyCtrl) + NavUpdateWindowingHighlightWindow(io.KeyShift ? +1 : -1); + if (!io.KeyCtrl) apply_focus_window = g.NavWindowingTarget; } // Keyboard: Press and Release ALT to toggle menu layer - // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of backend clearing releases all keys on ALT-TAB - if (IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Pressed)) + // - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer. + // - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway. + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + if (nav_keyboard_active && io.KeyMods == ImGuiKeyModFlags_Alt && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) == 0) + { g.NavWindowingToggleLayer = true; - if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && g.NavWindowingToggleLayer && IsNavInputTest(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) - if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) - apply_toggle_layer = true; + g.NavInputSource = ImGuiInputSource_Keyboard; + } + if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard) + { + // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370) + // We cancel toggling nav layer when other modifiers are pressed. (See #4439) + if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper) + g.NavWindowingToggleLayer = false; + + // Apply layer toggle on release + // Important: we don't assume that Alt was previously held in order to handle loss of focus when backend calls io.AddFocusEvent(false) + // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss. + if (!(io.KeyMods & ImGuiKeyModFlags_Alt) && (io.KeyModsPrev & ImGuiKeyModFlags_Alt) && g.NavWindowingToggleLayer) + if (g.ActiveId == 0 || g.ActiveIdAllowOverlap) + if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev)) + apply_toggle_layer = true; + if (!io.KeyAlt) + g.NavWindowingToggleLayer = false; + } // Move window if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) { ImVec2 move_delta; - if (g.NavInputSource == ImGuiInputSource_Keyboard && !g.IO.KeyShift) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); + if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift) + move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_RawKeyboard, ImGuiInputReadMode_Down); if (g.NavInputSource == ImGuiInputSource_Gamepad) move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); if (move_delta.x != 0.0f || move_delta.y != 0.0f) { const float NAV_MOVE_SPEED = 800.0f; - const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well + const float move_speed = ImFloor(NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow; SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always); MarkIniSettingsDirty(moving_window); @@ -9622,16 +10336,21 @@ static void ImGui::NavUpdateWindowing() if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) { ClearActiveID(); - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; + NavRestoreHighlightAfterMove(); apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); ClosePopupsOverWindow(apply_focus_window, false); FocusWindow(apply_focus_window); if (apply_focus_window->NavLastIds[0] == 0) NavInitWindow(apply_focus_window, false); - // If the window only has a menu layer, select it directly - if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu)) + // If the window has ONLY a menu layer (no main layer), select it directly + // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame, + // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since + // the target window as already been previewed once. + // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases, + // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask* + // won't be valid. + if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu)) g.NavLayer = ImGuiNavLayer_Menu; } if (apply_focus_window) @@ -9640,10 +10359,12 @@ static void ImGui::NavUpdateWindowing() // Apply menu/layer toggle if (apply_toggle_layer && g.NavWindow) { + ClearActiveID(); + // Move to parent menu if necessary ImGuiWindow* new_nav_window = g.NavWindow; while (new_nav_window->ParentWindow - && (new_nav_window->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) == 0 + && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) new_nav_window = new_nav_window->ParentWindow; @@ -9653,14 +10374,17 @@ static void ImGui::NavUpdateWindowing() FocusWindow(new_nav_window); new_nav_window->NavLastChildNavWindow = old_nav_window; } - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - // Reinitialize navigation when entering menu bar with the Alt key. - const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; - if (new_nav_layer == ImGuiNavLayer_Menu) - g.NavWindow->NavLastIds[new_nav_layer] = 0; - NavRestoreLayer(new_nav_layer); + // Toggle layer + const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; + if (new_nav_layer != g.NavLayer) + { + // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?) + if (new_nav_layer == ImGuiNavLayer_Menu) + g.NavWindow->NavLastIds[new_nav_layer] = 0; + NavRestoreLayer(new_nav_layer); + NavRestoreHighlightAfterMove(); + } } } @@ -9693,6 +10417,7 @@ void ImGui::NavUpdateWindowingOverlay() for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) { ImGuiWindow* window = g.WindowsFocusOrder[n]; + IM_ASSERT(window != NULL); // Fix static analyzers if (!IsWindowNavFocusable(window)) continue; const char* label = window->Name; @@ -9744,7 +10469,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) ImGuiID source_parent_id = 0; if (!(flags & ImGuiDragDropFlags_SourceExtern)) { - source_id = window->DC.LastItemId; + source_id = g.LastItemData.ID; if (source_id != 0) { // Common path: items with ID @@ -9752,34 +10477,33 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) return false; if (g.ActiveIdMouseButton != -1) mouse_button = g.ActiveIdMouseButton; - if (g.IO.MouseDown[mouse_button] == false) + if (g.IO.MouseDown[mouse_button] == false || window->SkipItems) return false; g.ActiveIdAllowOverlap = false; } else { // Uncommon path: items without ID - if (g.IO.MouseDown[mouse_button] == false) + if (g.IO.MouseDown[mouse_button] == false || window->SkipItems) + return false; + if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window)) return false; // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to: - // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride. + // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag. if (!(flags & ImGuiDragDropFlags_SourceAllowNullID)) { IM_ASSERT(0); return false; } - // Early out - if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window)) - return false; - - // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image() + // Magic fallback to handle items with no assigned ID, e.g. Text(), Image() // We build a throwaway ID based on current ID stack + relative AABB of items in window. - // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. + // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZINGG OF THE WIDGET, so if your widget moves your dragging operation will be canceled. // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. - source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect); - bool is_hovered = ItemHoverable(window->DC.LastItemRect, source_id); + // Rely on keeping other window->LastItemXXX fields intact. + source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect); + bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id); if (is_hovered && g.IO.MouseClicked[mouse_button]) { SetActiveID(source_id, window); @@ -9793,10 +10517,8 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) source_parent_id = window->IDStack.back(); source_drag_active = IsMouseDragging(mouse_button); - // Disable navigation and key inputs while dragging - g.ActiveIdUsingNavDirMask = ~(ImU32)0; - g.ActiveIdUsingNavInputMask = ~(ImU32)0; - g.ActiveIdUsingKeyInputMask = ~(ImU64)0; + // Disable navigation and key inputs while dragging + cancel existing request if any + SetActiveIdUsingNavAndKeys(); } else { @@ -9831,13 +10553,13 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) { ImGuiWindow* tooltip_window = g.CurrentWindow; - tooltip_window->SkipItems = true; + tooltip_window->Hidden = tooltip_window->SkipItems = true; tooltip_window->HiddenFramesCanSkipItems = 1; } } if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) - window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; + g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; return true; } @@ -9937,14 +10659,14 @@ bool ImGui::BeginDragDropTarget() return false; ImGuiWindow* window = g.CurrentWindow; - if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) + if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect)) return false; ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; - if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow) + if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow || window->SkipItems) return false; - const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect; - ImGuiID id = window->DC.LastItemId; + const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect; + ImGuiID id = g.LastItemData.ID; if (id == 0) id = window->GetIDFromRectangle(display_rect); if (g.DragDropPayload.SourceId == id) @@ -9986,17 +10708,11 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop } // Render default drop visuals + // FIXME-DRAGDROP: Settle on a proper default visuals for drop target. payload.Preview = was_accepted_previously; flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame) if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview) - { - // FIXME-DRAGDROP: Settle on a proper default visuals for drop target. - r.Expand(3.5f); - bool push_clip_rect = !window->ClipRect.Contains(r); - if (push_clip_rect) window->DrawList->PushClipRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1)); - window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); - if (push_clip_rect) window->DrawList->PopClipRect(); - } + window->DrawList->AddRect(r.Min - ImVec2(3.5f,3.5f), r.Max + ImVec2(3.5f, 3.5f), GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); g.DragDropAcceptFrameCount = g.FrameCount; payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting os window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased() @@ -10558,8 +11274,9 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); } IM_ASSERT(settings->ID == window->ID); - settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y); - settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y); + settings->Pos = ImVec2ih(window->Pos); + settings->Size = ImVec2ih(window->SizeFull); + settings->Collapsed = window->Collapsed; } @@ -10799,6 +11516,7 @@ static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} // - DebugNodeWindow() [Internal] // - DebugNodeWindowSettings() [Internal] // - DebugNodeWindowsList() [Internal] +// - DebugNodeWindowsListByBeginStackParent() [Internal] //----------------------------------------------------------------------------- #ifndef IMGUI_DISABLE_METRICS_WINDOW @@ -10868,24 +11586,29 @@ static void MetricsHelpMarker(const char* desc) } } +#ifndef IMGUI_DISABLE_DEMO_WINDOWS +namespace ImGui { void ShowFontAtlas(ImFontAtlas* atlas); } +#endif + void ImGui::ShowMetricsWindow(bool* p_open) { - if (!Begin("Dear ImGui Metrics/Debugger", p_open)) + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; + if (cfg->ShowStackTool) + ShowStackToolWindow(&cfg->ShowStackTool); + + if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1) { End(); return; } - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; - // Basic info Text("Dear ImGui %s", GetVersion()); Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); - Text("%d active windows (%d visible)", io.MetricsActiveWindows, io.MetricsRenderWindows); - Text("%d active allocations", io.MetricsActiveAllocations); + Text("%d visible windows, %d active allocations", io.MetricsRenderWindows, io.MetricsActiveAllocations); //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; } Separator(); @@ -10939,11 +11662,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Tools if (TreeNode("Tools")) { - // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. - if (Button("Item Picker..")) - DebugStartItemPicker(); + // Stack Tool is your best friend! + Checkbox("Show stack tool", &cfg->ShowStackTool); SameLine(); - MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash."); + MetricsHelpMarker("You can also call ImGui::ShowStackToolWindow() from your code."); Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder); Checkbox("Show windows rectangles", &cfg->ShowWindowsRects); @@ -10961,8 +11683,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) } Unindent(); } - Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); - Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes); Checkbox("Show tables rectangles", &cfg->ShowTablesRects); SameLine(); @@ -10970,10 +11690,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count); if (cfg->ShowTablesRects && g.NavWindow != NULL) { - for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++) + for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++) { - ImGuiTable* table = g.Tables.GetByIndex(table_n); - if (table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow)) + ImGuiTable* table = g.Tables.TryGetMapData(table_n); + if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow)) continue; BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name); @@ -11009,12 +11729,37 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } + // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. + if (Button("Item Picker..")) + DebugStartItemPicker(); + SameLine(); + MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash."); + TreePop(); } // Windows - DebugNodeWindowsList(&g.Windows, "Windows"); - //DebugNodeWindowsList(&g.WindowsFocusOrder, "WindowsFocusOrder"); + if (TreeNode("Windows", "Windows (%d)", g.Windows.Size)) + { + //SetNextItemOpen(true, ImGuiCond_Once); + DebugNodeWindowsList(&g.Windows, "By display order"); + DebugNodeWindowsList(&g.WindowsFocusOrder, "By focus order (root windows)"); + if (TreeNode("By submission order (begin stack)")) + { + // Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship! + ImVector& temp_buffer = g.WindowsTempSortBuffer; + temp_buffer.resize(0); + for (int i = 0; i < g.Windows.Size; i++) + if (g.Windows[i]->LastFrameActive + 1 >= g.FrameCount) + temp_buffer.push_back(g.Windows[i]); + struct Func { static int IMGUI_CDECL WindowComparerByBeginOrder(const void* lhs, const void* rhs) { return ((int)(*(const ImGuiWindow* const *)lhs)->BeginOrderWithinContext - (*(const ImGuiWindow* const*)rhs)->BeginOrderWithinContext); } }; + ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder); + DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL); + TreePop(); + } + + TreePop(); + } // DrawLists int drawlist_count = 0; @@ -11022,6 +11767,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount(); if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count)) { + Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); + Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes); for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) { ImGuiViewportP* viewport = g.Viewports[viewport_i]; @@ -11055,22 +11802,36 @@ void ImGui::ShowMetricsWindow(bool* p_open) } // Details for TabBars - if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetSize())) + if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount())) { - for (int n = 0; n < g.TabBars.GetSize(); n++) - DebugNodeTabBar(g.TabBars.GetByIndex(n), "TabBar"); + for (int n = 0; n < g.TabBars.GetMapSize(); n++) + if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n)) + { + PushID(tab_bar); + DebugNodeTabBar(tab_bar, "TabBar"); + PopID(); + } TreePop(); } // Details for Tables -#ifdef IMGUI_HAS_TABLE - if (TreeNode("Tables", "Tables (%d)", g.Tables.GetSize())) + if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount())) { - for (int n = 0; n < g.Tables.GetSize(); n++) - DebugNodeTable(g.Tables.GetByIndex(n)); + for (int n = 0; n < g.Tables.GetMapSize(); n++) + if (ImGuiTable* table = g.Tables.TryGetMapData(n)) + DebugNodeTable(table); TreePop(); } -#endif // #ifdef IMGUI_HAS_TABLE + + // Details for Fonts +#ifndef IMGUI_DISABLE_DEMO_WINDOWS + ImFontAtlas* atlas = g.IO.Fonts; + if (TreeNode("Fonts", "Fonts (%d)", atlas->Fonts.Size)) + { + ShowFontAtlas(atlas); + TreePop(); + } +#endif // Details for Docking #ifdef IMGUI_HAS_DOCK @@ -11110,14 +11871,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) TreePop(); } -#ifdef IMGUI_HAS_TABLE if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size())) { for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings)) DebugNodeTableSettings(settings); TreePop(); } -#endif // #ifdef IMGUI_HAS_TABLE #ifdef IMGUI_HAS_DOCK #endif // #ifdef IMGUI_HAS_DOCK @@ -11147,7 +11906,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) Indent(); Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]); Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); - Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not + Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %llX", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, g.ActiveIdUsingKeyInputMask); + Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); Unindent(); @@ -11157,7 +11917,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); Text("NavInputSource: %s", input_source_names[g.NavInputSource]); Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); - Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); + Text("NavActivateId/DownId/PressedId/InputId: %08X/%08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId, g.NavActivateInputId); + Text("NavActivateFlags: %04X", g.NavActivateFlags); Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId); Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); @@ -11191,14 +11952,13 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } -#ifdef IMGUI_HAS_TABLE // Overlay: Display Tables Rectangles if (cfg->ShowTablesRects) { - for (int table_n = 0; table_n < g.Tables.GetSize(); table_n++) + for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++) { - ImGuiTable* table = g.Tables.GetByIndex(table_n); - if (table->LastFrameActive < g.FrameCount - 1) + ImGuiTable* table = g.Tables.TryGetMapData(table_n); + if (table == NULL || table->LastFrameActive < g.FrameCount - 1) continue; ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow); if (cfg->ShowTablesRectsType >= TRT_ColumnsRect) @@ -11218,7 +11978,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) } } } -#endif // #ifdef IMGUI_HAS_TABLE #ifdef IMGUI_HAS_DOCK // Overlay: Display Docking info @@ -11230,6 +11989,25 @@ void ImGui::ShowMetricsWindow(bool* p_open) End(); } +// [DEBUG] List fonts in a font atlas and display its texture +void ImGui::ShowFontAtlas(ImFontAtlas* atlas) +{ + for (int i = 0; i < atlas->Fonts.Size; i++) + { + ImFont* font = atlas->Fonts[i]; + PushID(font); + DebugNodeFont(font); + PopID(); + } + if (TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) + { + ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); + Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col); + TreePop(); + } +} + // [DEBUG] Display contents of Columns void ImGui::DebugNodeColumns(ImGuiOldColumns* columns) { @@ -11260,7 +12038,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, } ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list - if (window && IsItemHovered()) + if (window && IsItemHovered() && fg_draw_list) fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) return; @@ -11339,16 +12117,17 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb) { IM_ASSERT(show_mesh || show_aabb); - ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; - ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset; // Draw wire-frame version of all triangles ImRect clip_rect = draw_cmd->ClipRect; ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); ImDrawListFlags backup_flags = out_draw_list->Flags; out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. - for (unsigned int idx_n = draw_cmd->IdxOffset; idx_n < draw_cmd->IdxOffset + draw_cmd->ElemCount; ) + for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; ) { + ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list + ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset; + ImVec2 triangle[3]; for (int n = 0; n < 3; n++, idx_n++) vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos)); @@ -11364,6 +12143,102 @@ void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, co out_draw_list->Flags = backup_flags; } +// [DEBUG] Display details for a single font, called by ShowStyleEditor(). +void ImGui::DebugNodeFont(ImFont* font) +{ + bool opened = TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)", + font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount); + SameLine(); + if (SmallButton("Set as default")) + GetIO().FontDefault = font; + if (!opened) + return; + + // Display preview text + PushFont(font); + Text("The quick brown fox jumps over the lazy dog"); + PopFont(); + + // Display details + SetNextItemWidth(GetFontSize() * 8); + DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); + SameLine(); MetricsHelpMarker( + "Note than the default embedded font is NOT meant to be scaled.\n\n" + "Font are currently rendered into bitmaps at a given size at the time of building the atlas. " + "You may oversample them to get some flexibility with scaling. " + "You can also render at multiple sizes and select which one to use at runtime.\n\n" + "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)"); + Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); + char c_str[5]; + Text("Fallback character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar); + Text("Ellipsis character: '%s' (U+%04X)", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar); + const int surface_sqrt = (int)ImSqrt((float)font->MetricsTotalSurface); + Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt); + for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) + if (font->ConfigData) + if (const ImFontConfig* cfg = &font->ConfigData[config_i]) + BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)", + config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y); + + // Display all glyphs of the fonts in separate pages of 256 characters + if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) + { + ImDrawList* draw_list = GetWindowDrawList(); + const ImU32 glyph_col = GetColorU32(ImGuiCol_Text); + const float cell_size = font->FontSize * 1; + const float cell_spacing = GetStyle().ItemSpacing.y; + for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256) + { + // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k) + // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT + // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here) + if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095)) + { + base += 4096 - 256; + continue; + } + + int count = 0; + for (unsigned int n = 0; n < 256; n++) + if (font->FindGlyphNoFallback((ImWchar)(base + n))) + count++; + if (count <= 0) + continue; + if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph")) + continue; + + // Draw a 16x16 grid of glyphs + ImVec2 base_pos = GetCursorScreenPos(); + for (unsigned int n = 0; n < 256; n++) + { + // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions + // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string. + ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); + ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); + const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); + draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); + if (glyph) + font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); + if (glyph && IsMouseHoveringRect(cell_p1, cell_p2)) + { + BeginTooltip(); + Text("Codepoint: U+%04X", base + n); + Separator(); + Text("Visible: %d", glyph->Visible); + Text("AdvanceX: %.1f", glyph->AdvanceX); + Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); + Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); + EndTooltip(); + } + } + Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); + TreePop(); + } + TreePop(); + } + TreePop(); +} + // [DEBUG] Display contents of ImGuiStorage void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label) { @@ -11386,9 +12261,16 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) const char* buf_end = buf + IM_ARRAYSIZE(buf); const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2); p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*"); - IM_UNUSED(p); + p += ImFormatString(p, buf_end - p, " { "); + for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + p += ImFormatString(p, buf_end - p, "%s'%s'", + tab_n > 0 ? ", " : "", (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???"); + } + p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } "); if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } - bool open = TreeNode(tab_bar, "%s", buf); + bool open = TreeNode(label, "%s", buf); if (!is_active) { PopStyleColor(); } if (is_active && IsItemHovered()) { @@ -11406,7 +12288,7 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2); if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine(); Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f", - tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "", tab->Offset, tab->Width, tab->ContentWidth); + tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth); PopID(); } TreePop(); @@ -11465,12 +12347,19 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : ""); BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); - BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); - BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); - if (!window->NavRectRel[0].IsInverted()) - BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); - else - BulletText("NavRectRel[0]: "); + for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) + { + ImRect r = window->NavRectRel[layer]; + if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y) + { + BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]); + continue; + } + BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y); + if (IsItemHovered()) + GetForegroundDrawList(window)->AddRect(r.Min + window->Pos, r.Max + window->Pos, IM_COL32(255, 255, 0, 255)); + } + BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); } @@ -11494,7 +12383,6 @@ void ImGui::DebugNodeWindowsList(ImVector* windows, const char* la { if (!TreeNode(label, "%s (%d)", label, windows->Size)) return; - Text("(In front-to-back order:)"); for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back { PushID((*windows)[i]); @@ -11504,12 +12392,214 @@ void ImGui::DebugNodeWindowsList(ImVector* windows, const char* la TreePop(); } +// FIXME-OPT: This is technically suboptimal, but it is simpler this way. +void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack) +{ + for (int i = 0; i < windows_size; i++) + { + ImGuiWindow* window = windows[i]; + if (window->ParentWindowInBeginStack != parent_in_begin_stack) + continue; + char buf[20]; + ImFormatString(buf, IM_ARRAYSIZE(buf), "[%04d] Window", window->BeginOrderWithinContext); + //BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name); + DebugNodeWindow(window, buf); + Indent(); + DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window); + Unindent(); + } +} + +//----------------------------------------------------------------------------- +// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) +//----------------------------------------------------------------------------- + +// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. +void ImGui::UpdateDebugToolItemPicker() +{ + ImGuiContext& g = *GImGui; + g.DebugItemPickerBreakId = 0; + if (!g.DebugItemPickerActive) + return; + + const ImGuiID hovered_id = g.HoveredIdPreviousFrame; + SetMouseCursor(ImGuiMouseCursor_Hand); + if (IsKeyPressedMap(ImGuiKey_Escape)) + g.DebugItemPickerActive = false; + if (IsMouseClicked(0) && hovered_id) + { + g.DebugItemPickerBreakId = hovered_id; + g.DebugItemPickerActive = false; + } + SetNextWindowBgAlpha(0.60f); + BeginTooltip(); + Text("HoveredId: 0x%08X", hovered_id); + Text("Press ESC to abort picking."); + TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); + EndTooltip(); +} + +// [DEBUG] Stack Tool: update queries. Called by NewFrame() +void ImGui::UpdateDebugToolStackQueries() +{ + ImGuiContext& g = *GImGui; + ImGuiStackTool* tool = &g.DebugStackTool; + + // Clear hook when stack tool is not visible + g.DebugHookIdInfo = 0; + if (g.FrameCount != tool->LastActiveFrame + 1) + return; + + // Update queries. The steps are: -1: query Stack, >= 0: query each stack item + // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time + const ImGuiID query_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId; + if (tool->QueryId != query_id) + { + tool->QueryId = query_id; + tool->StackLevel = -1; + tool->Results.resize(0); + } + if (query_id == 0) + return; + + // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result) + int stack_level = tool->StackLevel; + if (stack_level >= 0 && stack_level < tool->Results.Size) + if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2) + tool->StackLevel++; + + // Update hook + stack_level = tool->StackLevel; + if (stack_level == -1) + g.DebugHookIdInfo = query_id; + if (stack_level >= 0 && stack_level < tool->Results.Size) + { + g.DebugHookIdInfo = tool->Results[stack_level].ID; + tool->Results[stack_level].QueryFrameCount++; + } +} + +// [DEBUG] Stack tool: hooks called by GetID() family functions +void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStackTool* tool = &g.DebugStackTool; + + // Step 0: stack query + // This assume that the ID was computed with the current ID stack, which tends to be the case for our widget. + if (tool->StackLevel == -1) + { + tool->StackLevel++; + tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo()); + for (int n = 0; n < window->IDStack.Size + 1; n++) + tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id; + return; + } + + // Step 1+: query for individual level + IM_ASSERT(tool->StackLevel >= 0); + if (tool->StackLevel != window->IDStack.Size) + return; + ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel]; + IM_ASSERT(info->ID == id && info->QueryFrameCount > 0); + + int data_len; + switch (data_type) + { + case ImGuiDataType_S32: + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "%d", (int)(intptr_t)data_id); + break; + case ImGuiDataType_String: + data_len = data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)strlen((const char*)data_id); + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "\"%.*s\"", data_len, (const char*)data_id); + break; + case ImGuiDataType_Pointer: + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "(void*)0x%p", data_id); + break; + case ImGuiDataType_ID: + if (info->Desc[0] == 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one. + ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), "0x%08X [override]", id); + break; + default: + IM_ASSERT(0); + } + info->QuerySuccess = true; +} + +// Stack Tool: Display UI +void ImGui::ShowStackToolWindow(bool* p_open) +{ + ImGuiContext& g = *GImGui; + if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)) + SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver); + if (!Begin("Dear ImGui Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1) + { + End(); + return; + } + + // Display hovered/active status + const ImGuiID hovered_id = g.HoveredIdPreviousFrame; + const ImGuiID active_id = g.ActiveId; +#ifdef IMGUI_ENABLE_TEST_ENGINE + Text("HoveredId: 0x%08X (\"%s\"), ActiveId: 0x%08X (\"%s\")", hovered_id, hovered_id ? ImGuiTestEngine_FindItemDebugLabel(&g, hovered_id) : "", active_id, active_id ? ImGuiTestEngine_FindItemDebugLabel(&g, active_id) : ""); +#else + Text("HoveredId: 0x%08X, ActiveId: 0x%08X", hovered_id, active_id); +#endif + SameLine(); + MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details."); + + // Display decorated stack + ImGuiStackTool* tool = &g.DebugStackTool; + tool->LastActiveFrame = g.FrameCount; + if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders)) + { + const float id_width = CalcTextSize("0xDDDDDDDD").x; + TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width); + TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch); + TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width); + TableHeadersRow(); + for (int n = 0; n < tool->Results.Size; n++) + { + ImGuiStackLevelInfo* info = &tool->Results[n]; + TableNextColumn(); + Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0); + + TableNextColumn(); + ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? FindWindowByID(info->ID) : NULL; + if (window) // Source: window name (because the root ID don't call GetID() and so doesn't get hooked) + Text("\"%s\" [window]", window->Name); + else if (info->QuerySuccess) // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id) + TextUnformatted(info->Desc); + else if (tool->StackLevel >= tool->Results.Size) // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers. + { +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (const char* label = ImGuiTestEngine_FindItemDebugLabel(&g, info->ID)) // Source: ImGuiTestEngine's ItemInfo() + Text("??? \"%s\"", label); + else +#endif + TextUnformatted("???"); + } + + TableNextColumn(); + Text("0x%08X", info->ID); + if (n == tool->Results.Size - 1) + TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header)); + } + EndTable(); + } + End(); +} + #else void ImGui::ShowMetricsWindow(bool*) {} +void ImGui::ShowFontAtlas(ImFontAtlas*) {} void ImGui::DebugNodeColumns(ImGuiOldColumns*) {} void ImGui::DebugNodeDrawList(ImGuiWindow*, const ImDrawList*, const char*) {} void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {} +void ImGui::DebugNodeFont(ImFont*) {} void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {} void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {} void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {} @@ -11517,7 +12607,12 @@ void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {} void ImGui::DebugNodeWindowsList(ImVector*, const char*) {} void ImGui::DebugNodeViewport(ImGuiViewportP*) {} -#endif +void ImGui::ShowStackToolWindow(bool*) {} +void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {} +void ImGui::UpdateDebugToolItemPicker() {} +void ImGui::UpdateDebugToolStackQueries() {} + +#endif // #ifndef IMGUI_DISABLE_METRICS_WINDOW //----------------------------------------------------------------------------- diff --git a/external/imgui/src/imgui_demo.cpp b/r5dev/thirdparty/imgui/src/imgui_demo.cpp similarity index 95% rename from external/imgui/src/imgui_demo.cpp rename to r5dev/thirdparty/imgui/src/imgui_demo.cpp index 6cf6848e..ddb06af6 100644 --- a/external/imgui/src/imgui_demo.cpp +++ b/r5dev/thirdparty/imgui/src/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.83 WIP +// dear imgui, v1.86 // (demo code) // Help: @@ -75,7 +75,7 @@ Index of this file: #define _CRT_SECURE_NO_WARNINGS #endif -#include "imgui.h" +#include "thirdparty/imgui/include/imgui.h" #ifndef IMGUI_DISABLE // System includes @@ -92,7 +92,8 @@ Index of this file: // Visual Studio warnings #ifdef _MSC_VER -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). #endif // Clang/GCC warnings with -Weverything @@ -199,6 +200,14 @@ static void HelpMarker(const char* desc) } } +// Helper to wire demo markers located in code to a interactive browser +typedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section, void* user_data); +extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback; +extern void* GImGuiDemoMarkerCallbackUserData; +ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback = NULL; +void* GImGuiDemoMarkerCallbackUserData = NULL; +#define IMGUI_DEMO_MARKER(section) do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0) + // Helper to display basic user controls. void ImGui::ShowUserGuide() { @@ -209,7 +218,8 @@ void ImGui::ShowUserGuide() "(double-click to auto fit window to its contents)."); ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text."); ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields."); - if (io.FontAllowUserScaling) + ImGui::BulletText("CTRL+Tab to select a window."); + if (io.FontAllowUserScaling) ImGui::BulletText("CTRL+Mouse Wheel to zoom window contents."); ImGui::BulletText("While inputing text:\n"); ImGui::Indent(); @@ -227,7 +237,6 @@ void ImGui::ShowUserGuide() ImGui::BulletText("Return to input text into a widget."); ImGui::BulletText("Escape to deactivate a widget, close popup, exit child window."); ImGui::BulletText("Alt to jump to the menu layer of a window."); - ImGui::BulletText("CTRL+Tab to select a window."); ImGui::Unindent(); } @@ -293,10 +302,12 @@ void ImGui::ShowDemoWindow(bool* p_open) // Dear ImGui Apps (accessible from the "Tools" menu) static bool show_app_metrics = false; + static bool show_app_stack_tool = false; static bool show_app_style_editor = false; static bool show_app_about = false; if (show_app_metrics) { ImGui::ShowMetricsWindow(&show_app_metrics); } + if (show_app_stack_tool) { ImGui::ShowStackToolWindow(&show_app_stack_tool); } if (show_app_about) { ImGui::ShowAboutWindow(&show_app_about); } if (show_app_style_editor) { @@ -316,6 +327,7 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool no_nav = false; static bool no_background = false; static bool no_bring_to_front = false; + static bool unsaved_document = false; ImGuiWindowFlags window_flags = 0; if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; @@ -327,6 +339,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; if (no_background) window_flags |= ImGuiWindowFlags_NoBackground; if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus; + if (unsaved_document) window_flags |= ImGuiWindowFlags_UnsavedDocument; if (no_close) p_open = NULL; // Don't pass our bool* to Begin // We specify a default position/size in case there's no data in the .ini file. @@ -356,11 +369,13 @@ void ImGui::ShowDemoWindow(bool* p_open) { if (ImGui::BeginMenu("Menu")) { + IMGUI_DEMO_MARKER("Menu/File"); ShowExampleMenuFile(); ImGui::EndMenu(); } if (ImGui::BeginMenu("Examples")) { + IMGUI_DEMO_MARKER("Menu/Examples"); ImGui::MenuItem("Main menu bar", NULL, &show_app_main_menu_bar); ImGui::MenuItem("Console", NULL, &show_app_console); ImGui::MenuItem("Log", NULL, &show_app_log); @@ -376,9 +391,14 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::MenuItem("Documents", NULL, &show_app_documents); ImGui::EndMenu(); } + //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar! if (ImGui::BeginMenu("Tools")) { + IMGUI_DEMO_MARKER("Menu/Tools"); +#ifndef IMGUI_DISABLE_METRICS_WINDOW ImGui::MenuItem("Metrics/Debugger", NULL, &show_app_metrics); + ImGui::MenuItem("Stack Tool", NULL, &show_app_stack_tool); +#endif ImGui::MenuItem("Style Editor", NULL, &show_app_style_editor); ImGui::MenuItem("About Dear ImGui", NULL, &show_app_about); ImGui::EndMenu(); @@ -389,6 +409,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Text("dear imgui says hello. (%s)", IMGUI_VERSION); ImGui::Spacing(); + IMGUI_DEMO_MARKER("Help"); if (ImGui::CollapsingHeader("Help")) { ImGui::Text("ABOUT THIS DEMO:"); @@ -411,6 +432,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::ShowUserGuide(); } + IMGUI_DEMO_MARKER("Configuration"); if (ImGui::CollapsingHeader("Configuration")) { ImGuiIO& io = ImGui::GetIO(); @@ -451,11 +473,12 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Separator(); } + IMGUI_DEMO_MARKER("Configuration/Backend Flags"); if (ImGui::TreeNode("Backend Flags")) { HelpMarker( "Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\n" - "Here we expose then as read-only fields to avoid breaking interactions with your backend."); + "Here we expose them as read-only fields to avoid breaking interactions with your backend."); // Make a local copy to avoid modifying actual backend flags. ImGuiBackendFlags backend_flags = io.BackendFlags; @@ -467,6 +490,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Separator(); } + IMGUI_DEMO_MARKER("Configuration/Style"); if (ImGui::TreeNode("Style")) { HelpMarker("The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function."); @@ -475,6 +499,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Separator(); } + IMGUI_DEMO_MARKER("Configuration/Capture, Logging"); if (ImGui::TreeNode("Capture/Logging")) { HelpMarker( @@ -494,6 +519,7 @@ void ImGui::ShowDemoWindow(bool* p_open) } } + IMGUI_DEMO_MARKER("Window options"); if (ImGui::CollapsingHeader("Window options")) { if (ImGui::BeginTable("split", 3)) @@ -508,6 +534,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::TableNextColumn(); ImGui::Checkbox("No nav", &no_nav); ImGui::TableNextColumn(); ImGui::Checkbox("No background", &no_background); ImGui::TableNextColumn(); ImGui::Checkbox("No bring to front", &no_bring_to_front); + ImGui::TableNextColumn(); ImGui::Checkbox("Unsaved document", &unsaved_document); ImGui::EndTable(); } } @@ -526,11 +553,18 @@ void ImGui::ShowDemoWindow(bool* p_open) static void ShowDemoWindowWidgets() { + IMGUI_DEMO_MARKER("Widgets"); if (!ImGui::CollapsingHeader("Widgets")) return; + static bool disable_all = false; // The Checkbox for that is inside the "Disabled" section at the bottom + if (disable_all) + ImGui::BeginDisabled(); + + IMGUI_DEMO_MARKER("Widgets/Basic"); if (ImGui::TreeNode("Basic")) { + IMGUI_DEMO_MARKER("Widgets/Basic/Button"); static int clicked = 0; if (ImGui::Button("Button")) clicked++; @@ -540,15 +574,18 @@ static void ShowDemoWindowWidgets() ImGui::Text("Thanks for clicking me!"); } + IMGUI_DEMO_MARKER("Widgets/Basic/Checkbox"); static bool check = true; ImGui::Checkbox("checkbox", &check); + IMGUI_DEMO_MARKER("Widgets/Basic/RadioButton"); static int e = 0; ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine(); ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine(); ImGui::RadioButton("radio c", &e, 2); // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style. + IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Colored)"); for (int i = 0; i < 7; i++) { if (i > 0) @@ -570,6 +607,7 @@ static void ShowDemoWindowWidgets() ImGui::SameLine(); // Arrow buttons with Repeater + IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Repeating)"); static int counter = 0; float spacing = ImGui::GetStyle().ItemInnerSpacing.x; ImGui::PushButtonRepeat(true); @@ -580,6 +618,7 @@ static void ShowDemoWindowWidgets() ImGui::SameLine(); ImGui::Text("%d", counter); + IMGUI_DEMO_MARKER("Widgets/Basic/Tooltips"); ImGui::Text("Hover over me"); if (ImGui::IsItemHovered()) ImGui::SetTooltip("I am a tooltip"); @@ -596,12 +635,12 @@ static void ShowDemoWindowWidgets() } ImGui::Separator(); - ImGui::LabelText("label", "Value"); { // Using the _simplified_ one-liner Combo() api here // See "Combo" section for examples of how to use the more flexible BeginCombo()/EndCombo() api. + IMGUI_DEMO_MARKER("Widgets/Basic/Combo"); const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" }; static int item_current = 0; ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); @@ -612,6 +651,7 @@ static void ShowDemoWindowWidgets() { // To wire InputText() with std::string or any other custom string type, // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file. + IMGUI_DEMO_MARKER("Widgets/Basic/InputText"); static char str0[128] = "Hello, world!"; ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); ImGui::SameLine(); HelpMarker( @@ -630,6 +670,7 @@ static void ShowDemoWindowWidgets() static char str1[128] = ""; ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1)); + IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat"); static int i0 = 123; ImGui::InputInt("input int", &i0); ImGui::SameLine(); HelpMarker( @@ -654,6 +695,7 @@ static void ShowDemoWindowWidgets() } { + IMGUI_DEMO_MARKER("Widgets/Basic/DragInt, DragFloat"); static int i1 = 50, i2 = 42; ImGui::DragInt("drag int", &i1, 1); ImGui::SameLine(); HelpMarker( @@ -669,6 +711,7 @@ static void ShowDemoWindowWidgets() } { + IMGUI_DEMO_MARKER("Widgets/Basic/SliderInt, SliderFloat"); static int i1 = 0; ImGui::SliderInt("slider int", &i1, -1, 3); ImGui::SameLine(); HelpMarker("CTRL+click to input value."); @@ -677,12 +720,14 @@ static void ShowDemoWindowWidgets() ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); ImGui::SliderFloat("slider float (log)", &f2, -10.0f, 10.0f, "%.4f", ImGuiSliderFlags_Logarithmic); + IMGUI_DEMO_MARKER("Widgets/Basic/SliderAngle"); static float angle = 0.0f; ImGui::SliderAngle("slider angle", &angle); // Using the format string to display a name instead of an integer. // Here we completely omit '%d' from the format string, so it'll only display a name. // This technique can also be used with DragInt(). + IMGUI_DEMO_MARKER("Widgets/Basic/Slider (enum)"); enum Element { Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT }; static int elem = Element_Fire; const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" }; @@ -692,6 +737,7 @@ static void ShowDemoWindowWidgets() } { + IMGUI_DEMO_MARKER("Widgets/Basic/ColorEdit3, ColorEdit4"); static float col1[3] = { 1.0f, 0.0f, 0.2f }; static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f }; ImGui::ColorEdit3("color 1", col1); @@ -707,6 +753,7 @@ static void ShowDemoWindowWidgets() { // Using the _simplified_ one-liner ListBox() api here // See "List boxes" section for examples of how to use the more flexible BeginListBox()/EndListBox() api. + IMGUI_DEMO_MARKER("Widgets/Basic/ListBox"); const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; static int item_current = 1; ImGui::ListBox("listbox", &item_current, items, IM_ARRAYSIZE(items), 4); @@ -723,8 +770,10 @@ static void ShowDemoWindowWidgets() // if (once) // ImGui::Text("This will be displayed only once."); + IMGUI_DEMO_MARKER("Widgets/Trees"); if (ImGui::TreeNode("Trees")) { + IMGUI_DEMO_MARKER("Widgets/Trees/Basic trees"); if (ImGui::TreeNode("Basic trees")) { for (int i = 0; i < 5; i++) @@ -745,6 +794,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Trees/Advanced, with Selectable nodes"); if (ImGui::TreeNode("Advanced, with Selectable nodes")) { HelpMarker( @@ -772,6 +822,7 @@ static void ShowDemoWindowWidgets() for (int i = 0; i < 6; i++) { // Disable the default "open on single-click behavior" + set Selected flag according to our selection. + // To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection. ImGuiTreeNodeFlags node_flags = base_flags; const bool is_selected = (selection_mask & (1 << i)) != 0; if (is_selected) @@ -780,7 +831,7 @@ static void ShowDemoWindowWidgets() { // Items 0..2 are Tree Node bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); - if (ImGui::IsItemClicked()) + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) node_clicked = i; if (test_drag_and_drop && ImGui::BeginDragDropSource()) { @@ -801,7 +852,7 @@ static void ShowDemoWindowWidgets() // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text(). node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); - if (ImGui::IsItemClicked()) + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) node_clicked = i; if (test_drag_and_drop && ImGui::BeginDragDropSource()) { @@ -827,6 +878,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Collapsing Headers"); if (ImGui::TreeNode("Collapsing Headers")) { static bool closable_group = true; @@ -850,6 +902,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Bullets"); if (ImGui::TreeNode("Bullets")) { ImGui::BulletText("Bullet point 1"); @@ -864,8 +917,10 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text"); if (ImGui::TreeNode("Text")) { + IMGUI_DEMO_MARKER("Widgets/Text/Colored Text"); if (ImGui::TreeNode("Colorful Text")) { // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility. @@ -876,6 +931,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text/Word Wrapping"); if (ImGui::TreeNode("Word Wrapping")) { // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility. @@ -909,6 +965,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text/UTF-8 Text"); if (ImGui::TreeNode("UTF-8 Text")) { // UTF-8 test with Japanese characters @@ -935,6 +992,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Images"); if (ImGui::TreeNode("Images")) { ImGuiIO& io = ImGui::GetIO(); @@ -988,6 +1046,8 @@ static void ShowDemoWindowWidgets() ImGui::EndTooltip(); } } + + IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons"); ImGui::TextWrapped("And now some textured buttons.."); static int pressed_count = 0; for (int i = 0; i < 8; i++) @@ -1009,6 +1069,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Combo"); if (ImGui::TreeNode("Combo")) { // Expose flags as checkbox for the demo @@ -1025,8 +1086,8 @@ static void ShowDemoWindowWidgets() // stored in the object itself, etc.) const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; static int item_current_idx = 0; // Here we store our selection data as an index. - const char* combo_label = items[item_current_idx]; // Label to preview before opening the combo (technically it could be anything) - if (ImGui::BeginCombo("combo 1", combo_label, flags)) + const char* combo_preview_value = items[item_current_idx]; // Pass in the preview value visible before opening the combo (it could be anything) + if (ImGui::BeginCombo("combo 1", combo_preview_value, flags)) { for (int n = 0; n < IM_ARRAYSIZE(items); n++) { @@ -1042,10 +1103,12 @@ static void ShowDemoWindowWidgets() } // Simplified one-liner Combo() API, using values packed in a single constant string + // This is a convenience for when the selection set is small and known at compile-time. static int item_current_2 = 0; ImGui::Combo("combo 2 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); // Simplified one-liner Combo() using an array of const char* + // This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control. static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview ImGui::Combo("combo 3 (array)", &item_current_3, items, IM_ARRAYSIZE(items)); @@ -1057,6 +1120,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/List Boxes"); if (ImGui::TreeNode("List boxes")) { // Using the generic BeginListBox() API, you have full control over how to display the combo contents. @@ -1099,6 +1163,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables"); if (ImGui::TreeNode("Selectables")) { // Selectable() has 2 overloads: @@ -1107,18 +1172,20 @@ static void ShowDemoWindowWidgets() // - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases) // The earlier is more flexible, as in real application your selection may be stored in many different ways // and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc). + IMGUI_DEMO_MARKER("Widgets/Selectables/Basic"); if (ImGui::TreeNode("Basic")) { static bool selection[5] = { false, true, false, false, false }; ImGui::Selectable("1. I am selectable", &selection[0]); ImGui::Selectable("2. I am selectable", &selection[1]); - ImGui::Text("3. I am not selectable"); + ImGui::Text("(I am not selectable)"); ImGui::Selectable("4. I am selectable", &selection[3]); if (ImGui::Selectable("5. I am double clickable", selection[4], ImGuiSelectableFlags_AllowDoubleClick)) if (ImGui::IsMouseDoubleClicked(0)) selection[4] = !selection[4]; ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Single Selection"); if (ImGui::TreeNode("Selection State: Single Selection")) { static int selected = -1; @@ -1131,6 +1198,7 @@ static void ShowDemoWindowWidgets() } ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Multiple Selection"); if (ImGui::TreeNode("Selection State: Multiple Selection")) { HelpMarker("Hold CTRL and click to select multiple items."); @@ -1148,6 +1216,7 @@ static void ShowDemoWindowWidgets() } ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more text into the same line"); if (ImGui::TreeNode("Rendering more text into the same line")) { // Using the Selectable() override that takes "bool* p_selected" parameter, @@ -1158,11 +1227,12 @@ static void ShowDemoWindowWidgets() ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/In columns"); if (ImGui::TreeNode("In columns")) { static bool selected[10] = {}; - if (ImGui::BeginTable("split1", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings)) + if (ImGui::BeginTable("split1", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders)) { for (int i = 0; i < 10; i++) { @@ -1173,8 +1243,8 @@ static void ShowDemoWindowWidgets() } ImGui::EndTable(); } - ImGui::Separator(); - if (ImGui::BeginTable("split2", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings)) + ImGui::Spacing(); + if (ImGui::BeginTable("split2", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders)) { for (int i = 0; i < 10; i++) { @@ -1192,6 +1262,7 @@ static void ShowDemoWindowWidgets() } ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Grid"); if (ImGui::TreeNode("Grid")) { static char selected[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; @@ -1224,6 +1295,7 @@ static void ShowDemoWindowWidgets() ImGui::PopStyleVar(); ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Alignment"); if (ImGui::TreeNode("Alignment")) { HelpMarker( @@ -1251,8 +1323,10 @@ static void ShowDemoWindowWidgets() // To wire InputText() with std::string or any other custom string type, // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file. + IMGUI_DEMO_MARKER("Widgets/Text Input"); if (ImGui::TreeNode("Text Input")) { + IMGUI_DEMO_MARKER("Widgets/Text Input/Multi-line Text Input"); if (ImGui::TreeNode("Multi-line Text Input")) { // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize @@ -1278,6 +1352,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text Input/Filtered Text Input"); if (ImGui::TreeNode("Filtered Text Input")) { struct TextFilters @@ -1300,6 +1375,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text Input/Password input"); if (ImGui::TreeNode("Password Input")) { static char password[64] = "password123"; @@ -1366,6 +1442,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text Input/Resize Callback"); if (ImGui::TreeNode("Resize Callback")) { // To wire InputText() with std::string or any other custom string type, @@ -1412,8 +1489,10 @@ static void ShowDemoWindowWidgets() } // Tabs + IMGUI_DEMO_MARKER("Widgets/Tabs"); if (ImGui::TreeNode("Tabs")) { + IMGUI_DEMO_MARKER("Widgets/Tabs/Basic"); if (ImGui::TreeNode("Basic")) { ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; @@ -1440,6 +1519,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Tabs/Advanced & Close Button"); if (ImGui::TreeNode("Advanced & Close Button")) { // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0). @@ -1482,6 +1562,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Tabs/TabItemButton & Leading-Trailing flags"); if (ImGui::TreeNode("TabItemButton & Leading/Trailing flags")) { static ImVector active_tabs; @@ -1551,16 +1632,19 @@ static void ShowDemoWindowWidgets() } // Plot/Graph widgets are not very good. - // Consider writing your own, or using a third-party one, see: - // - ImPlot https://github.com/epezent/implot - // - others https://github.com/ocornut/imgui/wiki/Useful-Widgets - if (ImGui::TreeNode("Plots Widgets")) + // Consider using a third-party library such as ImPlot: https://github.com/epezent/implot + // (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions) + IMGUI_DEMO_MARKER("Widgets/Plotting"); + if (ImGui::TreeNode("Plotting")) { static bool animate = true; ImGui::Checkbox("Animate", &animate); + // Plot as lines and plot as histogram + IMGUI_DEMO_MARKER("Widgets/Plotting/PlotLines, PlotHistogram"); static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); + ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); // Fill an array of contiguous float values to plot // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float @@ -1590,7 +1674,6 @@ static void ShowDemoWindowWidgets() sprintf(overlay, "avg %f", average); ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f)); } - ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f)); // Use functions to generate output // FIXME: This is rather awkward because current plot API only pass in indices. @@ -1602,7 +1685,7 @@ static void ShowDemoWindowWidgets() }; static int func_type = 0, display_count = 70; ImGui::Separator(); - ImGui::SetNextItemWidth(100); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::Combo("func", &func_type, "Sin\0Saw\0"); ImGui::SameLine(); ImGui::SliderInt("Sample count", &display_count, 1, 400); @@ -1612,6 +1695,7 @@ static void ShowDemoWindowWidgets() ImGui::Separator(); // Animate a simple progress bar + IMGUI_DEMO_MARKER("Widgets/Plotting/ProgressBar"); static float progress = 0.0f, progress_dir = 1.0f; if (animate) { @@ -1633,6 +1717,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Color"); if (ImGui::TreeNode("Color/Picker Widgets")) { static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f); @@ -1649,18 +1734,22 @@ static void ShowDemoWindowWidgets() ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); ImGuiColorEditFlags misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); + IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit"); ImGui::Text("Color widget:"); ImGui::SameLine(); HelpMarker( "Click on the color square to open a color picker.\n" "CTRL+click on individual component to input value.\n"); ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); + IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)"); ImGui::Text("Color widget HSV with Alpha:"); ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | misc_flags); + IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (float display)"); ImGui::Text("Color widget with Float Display:"); ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); + IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with Picker)"); ImGui::Text("Color button with Picker:"); ImGui::SameLine(); HelpMarker( "With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n" @@ -1668,6 +1757,7 @@ static void ShowDemoWindowWidgets() "be used for the tooltip and picker popup."); ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); + IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with custom Picker popup)"); ImGui::Text("Color button with Custom Picker Popup:"); // Generate a default palette. The palette will persist and can be edited. @@ -1735,11 +1825,13 @@ static void ShowDemoWindowWidgets() ImGui::EndPopup(); } + IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (simple)"); ImGui::Text("Color button only:"); static bool no_border = false; ImGui::Checkbox("ImGuiColorEditFlags_NoBorder", &no_border); ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80)); + IMGUI_DEMO_MARKER("Widgets/Color/ColorPicker"); ImGui::Text("Color picker:"); static bool alpha = true; static bool alpha_bar = true; @@ -1807,6 +1899,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Drag and Slider Flags"); if (ImGui::TreeNode("Drag/Slider Flags")) { // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same! @@ -1840,6 +1933,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Range Widgets"); if (ImGui::TreeNode("Range Widgets")) { static float begin = 10, end = 90; @@ -1850,6 +1944,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Data Types"); if (ImGui::TreeNode("Data Types")) { // DragScalar/InputScalar/SliderScalar functions allow various data types @@ -1899,6 +1994,7 @@ static void ShowDemoWindowWidgets() const float drag_speed = 0.2f; static bool drag_clamp = false; + IMGUI_DEMO_MARKER("Widgets/Data Types/Drags"); ImGui::Text("Drags:"); ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); HelpMarker( @@ -1917,6 +2013,7 @@ static void ShowDemoWindowWidgets() ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams"); ImGui::DragScalar("drag double log",ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", ImGuiSliderFlags_Logarithmic); + IMGUI_DEMO_MARKER("Widgets/Data Types/Sliders"); ImGui::Text("Sliders"); ImGui::SliderScalar("slider s8 full", ImGuiDataType_S8, &s8_v, &s8_min, &s8_max, "%d"); ImGui::SliderScalar("slider u8 full", ImGuiDataType_U8, &u8_v, &u8_min, &u8_max, "%u"); @@ -1949,6 +2046,7 @@ static void ShowDemoWindowWidgets() ImGui::SliderScalar("slider s64 reverse", ImGuiDataType_S64, &s64_v, &s64_fifty, &s64_zero, "%" IM_PRId64); ImGui::SliderScalar("slider u64 reverse", ImGuiDataType_U64, &u64_v, &u64_fifty, &u64_zero, "%" IM_PRIu64 " ms"); + IMGUI_DEMO_MARKER("Widgets/Data Types/Inputs"); static bool inputs_step = true; ImGui::Text("Inputs"); ImGui::Checkbox("Show step buttons", &inputs_step); @@ -1968,6 +2066,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Multi-component Widgets"); if (ImGui::TreeNode("Multi-component Widgets")) { static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; @@ -1999,6 +2098,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Vertical Sliders"); if (ImGui::TreeNode("Vertical Sliders")) { const float spacing = 4; @@ -2063,8 +2163,10 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Drag and drop"); if (ImGui::TreeNode("Drag and Drop")) { + IMGUI_DEMO_MARKER("Widgets/Drag and drop/Standard widgets"); if (ImGui::TreeNode("Drag and drop in standard widgets")) { // ColorEdit widgets automatically act as drag source and drag target. @@ -2079,6 +2181,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Drag and drop/Copy-swap items"); if (ImGui::TreeNode("Drag and drop to copy/swap items")) { enum Mode @@ -2146,6 +2249,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)"); if (ImGui::TreeNode("Drag to reorder items (simple)")) { // Simple reordering @@ -2175,38 +2279,45 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } - if (ImGui::TreeNode("Querying Status (Edited/Active/Focused/Hovered etc.)")) + IMGUI_DEMO_MARKER("Widgets/Querying Item Status (Edited,Active,Hovered etc.)"); + if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)")) { // Select an item type const char* item_names[] = { - "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputFloat", - "InputFloat3", "ColorEdit4", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox" + "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputTextMultiline", "InputFloat", + "InputFloat3", "ColorEdit4", "Selectable", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox" }; - static int item_type = 1; + static int item_type = 4; + static bool item_disabled = false; ImGui::Combo("Item Type", &item_type, item_names, IM_ARRAYSIZE(item_names), IM_ARRAYSIZE(item_names)); ImGui::SameLine(); HelpMarker("Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered()."); + ImGui::Checkbox("Item Disabled", &item_disabled); // Submit selected item item so we can query their status in the code following it. bool ret = false; static bool b = false; static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f }; static char str[16] = {}; + if (item_disabled) + ImGui::BeginDisabled(true); if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button if (item_type == 2) { ImGui::PushButtonRepeat(true); ret = ImGui::Button("ITEM: Button"); ImGui::PopButtonRepeat(); } // Testing button (with repeater) if (item_type == 3) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); } // Testing checkbox if (item_type == 4) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item if (item_type == 5) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which handles tabbing) - if (item_type == 6) { ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f); } // Testing +/- buttons on scalar input - if (item_type == 7) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) - if (item_type == 8) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) - if (item_type == 9) { ret = ImGui::MenuItem("ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy) - if (item_type == 10){ ret = ImGui::TreeNode("ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node - if (item_type == 11){ ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy. - if (item_type == 12){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", ¤t, items, IM_ARRAYSIZE(items)); } - if (item_type == 13){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } + if (item_type == 6) { ret = ImGui::InputTextMultiline("ITEM: InputTextMultiline", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which uses a child window) + if (item_type == 7) { ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f); } // Testing +/- buttons on scalar input + if (item_type == 8) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) + if (item_type == 9) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) + if (item_type == 10){ ret = ImGui::Selectable("ITEM: Selectable"); } // Testing selectable item + if (item_type == 11){ ret = ImGui::MenuItem("ITEM: MenuItem"); } // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy) + if (item_type == 12){ ret = ImGui::TreeNode("ITEM: TreeNode"); if (ret) ImGui::TreePop(); } // Testing tree node + if (item_type == 13){ ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy. + if (item_type == 14){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", ¤t, items, IM_ARRAYSIZE(items)); } + if (item_type == 15){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } // Display the values of IsItemHovered() and other common item state functions. // Note that the ImGuiHoveredFlags_XXX flags can be combined. @@ -2219,6 +2330,7 @@ static void ShowDemoWindowWidgets() "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n" "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n" "IsItemHovered(_AllowWhenOverlapped) = %d\n" + "IsItemHovered(_AllowWhenDisabled) = %d\n" "IsItemHovered(_RectOnly) = %d\n" "IsItemActive() = %d\n" "IsItemEdited() = %d\n" @@ -2237,6 +2349,7 @@ static void ShowDemoWindowWidgets() ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped), + ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled), ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly), ImGui::IsItemActive(), ImGui::IsItemEdited(), @@ -2251,43 +2364,67 @@ static void ShowDemoWindowWidgets() ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y ); + if (item_disabled) + ImGui::EndDisabled(); + + char buf[1] = ""; + ImGui::InputText("unused", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly); + ImGui::SameLine(); + HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status."); + + ImGui::TreePop(); + } + + IMGUI_DEMO_MARKER("Widgets/Querying Window Status (Focused,Hovered etc.)"); + if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)")) + { static bool embed_all_inside_a_child_window = false; - ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window); + ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window); if (embed_all_inside_a_child_window) ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), true); // Testing IsWindowFocused() function with its various flags. - // Note that the ImGuiFocusedFlags_XXX flags can be combined. ImGui::BulletText( "IsWindowFocused() = %d\n" "IsWindowFocused(_ChildWindows) = %d\n" + "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" + "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_RootWindow) = %d\n" + "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowFocused(_AnyWindow) = %d\n", ImGui::IsWindowFocused(), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); // Testing IsWindowHovered() function with its various flags. - // Note that the ImGuiHoveredFlags_XXX flags can be combined. ImGui::BulletText( "IsWindowHovered() = %d\n" "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" "IsWindowHovered(_ChildWindows) = %d\n" + "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" - "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" + "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_RootWindow) = %d\n" + "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n" + "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_AnyWindow) = %d\n", ImGui::IsWindowHovered(), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); ImGui::BeginChild("child", ImVec2(0, 50), true); @@ -2296,9 +2433,6 @@ static void ShowDemoWindowWidgets() if (embed_all_inside_a_child_window) ImGui::EndChild(); - static char unused_str[] = "This widget is only here to be able to tab-out of the widgets above."; - ImGui::InputText("unused", unused_str, IM_ARRAYSIZE(unused_str), ImGuiInputTextFlags_ReadOnly); - // Calling IsItemHovered() after begin returns the hovered status of the title bar. // This is useful in particular if you want to create a context menu associated to the title bar of a window. static bool test_window = false; @@ -2320,13 +2454,28 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + + // Demonstrate BeginDisabled/EndDisabled using a checkbox located at the bottom of the section (which is a bit odd: + // logically we'd have this checkbox at the top of the section, but we don't want this feature to steal that space) + if (disable_all) + ImGui::EndDisabled(); + + IMGUI_DEMO_MARKER("Widgets/Disable Block"); + if (ImGui::TreeNode("Disable block")) + { + ImGui::Checkbox("Disable entire section above", &disable_all); + ImGui::SameLine(); HelpMarker("Demonstrate using BeginDisabled()/EndDisabled() across this section."); + ImGui::TreePop(); + } } static void ShowDemoWindowLayout() { + IMGUI_DEMO_MARKER("Layout"); if (!ImGui::CollapsingHeader("Layout & Scrolling")) return; + IMGUI_DEMO_MARKER("Layout/Child windows"); if (ImGui::TreeNode("Child windows")) { HelpMarker("Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window."); @@ -2340,7 +2489,7 @@ static void ShowDemoWindowLayout() ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar; if (disable_mouse_wheel) window_flags |= ImGuiWindowFlags_NoScrollWithMouse; - ImGui::BeginChild("ChildL", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f, 260), false, window_flags); + ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), false, window_flags); for (int i = 0; i < 100; i++) ImGui::Text("%04d: scrollable region", i); ImGui::EndChild(); @@ -2389,10 +2538,10 @@ static void ShowDemoWindowLayout() // You can also call SetNextWindowPos() to position the child window. The parent window will effectively // layout from this position. // - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from - // the POV of the parent window). See 'Demo->Querying Status (Active/Focused/Hovered etc.)' for details. + // the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details. { static int offset_x = 0; - ImGui::SetNextItemWidth(100); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x); @@ -2412,17 +2561,18 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Widgets Width"); if (ImGui::TreeNode("Widgets Width")) { + static float f = 0.0f; + static bool show_indented_items = true; + ImGui::Checkbox("Show indented items", &show_indented_items); + // Use SetNextItemWidth() to set the width of a single upcoming item. // Use PushItemWidth()/PopItemWidth() to set the width of a group of items. // In real code use you'll probably want to choose width values that are proportional to your font size // e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc. - static float f = 0.0f; - static bool show_indented_items = true; - ImGui::Checkbox("Show indented items", &show_indented_items); - ImGui::Text("SetNextItemWidth/PushItemWidth(100)"); ImGui::SameLine(); HelpMarker("Fixed width."); ImGui::PushItemWidth(100); @@ -2488,11 +2638,13 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout"); if (ImGui::TreeNode("Basic Horizontal Layout")) { ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)"); // Text + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine"); ImGui::Text("Two items: Hello"); ImGui::SameLine(); ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); @@ -2513,6 +2665,7 @@ static void ShowDemoWindowLayout() ImGui::Text("can fit within a text block."); // Aligned to arbitrary position. Easy/cheap column. + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (with offset)"); ImGui::Text("Aligned"); ImGui::SameLine(150); ImGui::Text("x=150"); ImGui::SameLine(300); ImGui::Text("x=300"); @@ -2521,6 +2674,7 @@ static void ShowDemoWindowLayout() ImGui::SameLine(300); ImGui::SmallButton("x=300"); // Checkbox + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (more)"); static bool c1 = false, c2 = false, c3 = false, c4 = false; ImGui::Checkbox("My", &c1); ImGui::SameLine(); ImGui::Checkbox("Tailor", &c2); ImGui::SameLine(); @@ -2552,6 +2706,7 @@ static void ShowDemoWindowLayout() ImGui::PopItemWidth(); // Dummy + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Dummy"); ImVec2 button_sz(40, 40); ImGui::Button("A", button_sz); ImGui::SameLine(); ImGui::Dummy(button_sz); ImGui::SameLine(); @@ -2559,7 +2714,8 @@ static void ShowDemoWindowLayout() // Manually wrapping // (we should eventually provide this as an automatic layout feature, but for now you can do it manually) - ImGui::Text("Manually wrapping:"); + IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Manual wrapping"); + ImGui::Text("Manual wrapping:"); ImGuiStyle& style = ImGui::GetStyle(); int buttons_count = 20; float window_visible_x2 = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x; @@ -2577,6 +2733,7 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Groups"); if (ImGui::TreeNode("Groups")) { HelpMarker( @@ -2624,6 +2781,7 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Text Baseline Alignment"); if (ImGui::TreeNode("Text Baseline Alignment")) { { @@ -2742,9 +2900,11 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Scrolling"); if (ImGui::TreeNode("Scrolling")) { // Vertical scroll functions + IMGUI_DEMO_MARKER("Layout/Scrolling/Vertical"); HelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position."); static int track_item = 50; @@ -2817,6 +2977,7 @@ static void ShowDemoWindowLayout() ImGui::PopID(); // Horizontal scroll functions + IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal"); ImGui::Spacing(); HelpMarker( "Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n" @@ -2862,6 +3023,7 @@ static void ShowDemoWindowLayout() ImGui::PopID(); // Miscellaneous Horizontal Scrolling Demo + IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal (more)"); HelpMarker( "Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\n" "You may want to also explicitly specify content width by using SetNextWindowContentWidth() before Begin()."); @@ -2936,6 +3098,7 @@ static void ShowDemoWindowLayout() if (explicit_content_size) ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f)); ImGui::Begin("Horizontal contents size demo window", &show_horizontal_contents_size_demo_window, show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0); + IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal contents size demo window"); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0)); HelpMarker("Test of different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\nUse 'Metrics->Tools->Show windows rectangles' to visualize rectangles."); @@ -3022,6 +3185,7 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Layout/Clipping"); if (ImGui::TreeNode("Clipping")) { static ImVec2 size(100.0f, 100.0f); @@ -3090,6 +3254,7 @@ static void ShowDemoWindowLayout() static void ShowDemoWindowPopups() { + IMGUI_DEMO_MARKER("Popups"); if (!ImGui::CollapsingHeader("Popups & Modal windows")) return; @@ -3111,6 +3276,7 @@ static void ShowDemoWindowPopups() // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state. // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below. + IMGUI_DEMO_MARKER("Popups/Popups"); if (ImGui::TreeNode("Popups")) { ImGui::TextWrapped( @@ -3179,17 +3345,33 @@ static void ShowDemoWindowPopups() } // Call the more complete ShowExampleMenuFile which we use in various places of this demo - if (ImGui::Button("File Menu..")) + if (ImGui::Button("With a menu..")) ImGui::OpenPopup("my_file_popup"); - if (ImGui::BeginPopup("my_file_popup")) + if (ImGui::BeginPopup("my_file_popup", ImGuiWindowFlags_MenuBar)) { - ShowExampleMenuFile(); + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + ShowExampleMenuFile(); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Edit")) + { + ImGui::MenuItem("Dummy"); + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + ImGui::Text("Hello from popup!"); + ImGui::Button("This is a dummy button.."); ImGui::EndPopup(); } ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Popups/Context menus"); if (ImGui::TreeNode("Context menus")) { HelpMarker("\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier."); @@ -3274,6 +3456,7 @@ static void ShowDemoWindowPopups() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Popups/Modals"); if (ImGui::TreeNode("Modals")) { ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside."); @@ -3349,6 +3532,7 @@ static void ShowDemoWindowPopups() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Popups/Menus inside a regular window"); if (ImGui::TreeNode("Menus inside a regular window")) { ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!"); @@ -3495,6 +3679,7 @@ static void EditTableSizingFlags(ImGuiTableFlags* p_flags) static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags) { + ImGui::CheckboxFlags("_Disabled", p_flags, ImGuiTableColumnFlags_Disabled); ImGui::SameLine(); HelpMarker("Master disable flag (also hide from context menu)"); ImGui::CheckboxFlags("_DefaultHide", p_flags, ImGuiTableColumnFlags_DefaultHide); ImGui::CheckboxFlags("_DefaultSort", p_flags, ImGuiTableColumnFlags_DefaultSort); if (ImGui::CheckboxFlags("_WidthStretch", p_flags, ImGuiTableColumnFlags_WidthStretch)) @@ -3508,6 +3693,7 @@ static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags) ImGui::CheckboxFlags("_NoSort", p_flags, ImGuiTableColumnFlags_NoSort); ImGui::CheckboxFlags("_NoSortAscending", p_flags, ImGuiTableColumnFlags_NoSortAscending); ImGui::CheckboxFlags("_NoSortDescending", p_flags, ImGuiTableColumnFlags_NoSortDescending); + ImGui::CheckboxFlags("_NoHeaderLabel", p_flags, ImGuiTableColumnFlags_NoHeaderLabel); ImGui::CheckboxFlags("_NoHeaderWidth", p_flags, ImGuiTableColumnFlags_NoHeaderWidth); ImGui::CheckboxFlags("_PreferSortAscending", p_flags, ImGuiTableColumnFlags_PreferSortAscending); ImGui::CheckboxFlags("_PreferSortDescending", p_flags, ImGuiTableColumnFlags_PreferSortDescending); @@ -3526,6 +3712,7 @@ static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags) static void ShowDemoWindowTables() { //ImGui::SetNextItemOpen(true, ImGuiCond_Once); + IMGUI_DEMO_MARKER("Tables"); if (!ImGui::CollapsingHeader("Tables & Columns")) return; @@ -3565,6 +3752,7 @@ static void ShowDemoWindowTables() // Demos if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Basic"); if (ImGui::TreeNode("Basic")) { // Here we will showcase three different ways to output a table. @@ -3626,6 +3814,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Borders, background"); if (ImGui::TreeNode("Borders, background")) { // Expose a few Borders related flags interactively @@ -3696,6 +3885,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Resizable, stretch"); if (ImGui::TreeNode("Resizable, stretch")) { // By default, if we don't enable ScrollX the sizing policy for each columns is "Stretch" @@ -3725,6 +3915,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Resizable, fixed"); if (ImGui::TreeNode("Resizable, fixed")) { // Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set) @@ -3758,6 +3949,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Resizable, mixed"); if (ImGui::TreeNode("Resizable, mixed")) { HelpMarker( @@ -3807,6 +3999,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Reorderable, hideable, with headers"); if (ImGui::TreeNode("Reorderable, hideable, with headers")) { HelpMarker( @@ -3864,6 +4057,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Padding"); if (ImGui::TreeNode("Padding")) { // First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding. @@ -3972,6 +4166,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Explicit widths"); if (ImGui::TreeNode("Sizing policies")) { static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody; @@ -4075,6 +4270,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Vertical scrolling, with clipping"); if (ImGui::TreeNode("Vertical scrolling, with clipping")) { HelpMarker("Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\n\nWe also demonstrate using ImGuiListClipper to virtualize the submission of many items."); @@ -4117,6 +4313,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Horizontal scrolling"); if (ImGui::TreeNode("Horizontal scrolling")) { HelpMarker( @@ -4205,6 +4402,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Columns flags"); if (ImGui::TreeNode("Columns flags")) { // Create a first table just to show all the options/flags we want to make visible in our example! @@ -4269,6 +4467,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Columns widths"); if (ImGui::TreeNode("Columns widths")) { HelpMarker("Using TableSetupColumn() to setup default width."); @@ -4334,6 +4533,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Nested tables"); if (ImGui::TreeNode("Nested tables")) { HelpMarker("This demonstrate embedding a table into another table cell."); @@ -4378,6 +4578,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Row height"); if (ImGui::TreeNode("Row height")) { HelpMarker("You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\nWe cannot honor a _maximum_ row height as that would requires a unique clipping rectangle per row."); @@ -4397,6 +4598,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Outer size"); if (ImGui::TreeNode("Outer size")) { // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY @@ -4463,6 +4665,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Background color"); if (ImGui::TreeNode("Background color")) { static ImGuiTableFlags flags = ImGuiTableFlags_RowBg; @@ -4520,6 +4723,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Tree view"); if (ImGui::TreeNode("Tree view")) { static ImGuiTableFlags flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; @@ -4591,6 +4795,7 @@ static void ShowDemoWindowTables() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Item width"); if (ImGui::TreeNode("Item width")) { HelpMarker( @@ -4636,6 +4841,7 @@ static void ShowDemoWindowTables() // Demonstrate using TableHeader() calls instead of TableHeadersRow() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Custom headers"); if (ImGui::TreeNode("Custom headers")) { const int COLUMNS_COUNT = 3; @@ -4683,6 +4889,7 @@ static void ShowDemoWindowTables() // Demonstrate creating custom context menus inside columns, while playing it nice with context menus provided by TableHeadersRow()/TableHeader() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Context menus"); if (ImGui::TreeNode("Context menus")) { HelpMarker("By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\nUsing ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body."); @@ -4789,6 +4996,7 @@ static void ShowDemoWindowTables() // Demonstrate creating multiple tables with the same ID if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Synced instances"); if (ImGui::TreeNode("Synced instances")) { HelpMarker("Multiple tables with the same identifier will share their settings, width, visibility, order etc."); @@ -4824,6 +5032,7 @@ static void ShowDemoWindowTables() }; if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Sorting"); if (ImGui::TreeNode("Sorting")) { // Create item list @@ -4911,6 +5120,7 @@ static void ShowDemoWindowTables() //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // [DEBUG] if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Advanced"); if (ImGui::TreeNode("Advanced")) { static ImGuiTableFlags flags = @@ -5227,6 +5437,7 @@ static void ShowDemoWindowTables() // [2020: Columns are under-featured and not maintained. Prefer using the more flexible and powerful BeginTable() API!] static void ShowDemoWindowColumns() { + IMGUI_DEMO_MARKER("Columns (legacy API)"); bool open = ImGui::TreeNode("Legacy Columns API"); ImGui::SameLine(); HelpMarker("Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!"); @@ -5234,6 +5445,7 @@ static void ShowDemoWindowColumns() return; // Basic columns + IMGUI_DEMO_MARKER("Columns (legacy API)/Basic"); if (ImGui::TreeNode("Basic")) { ImGui::Text("Without border:"); @@ -5278,6 +5490,7 @@ static void ShowDemoWindowColumns() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Columns (legacy API)/Borders"); if (ImGui::TreeNode("Borders")) { // NB: Future columns API should allow automatic horizontal borders. @@ -5313,6 +5526,7 @@ static void ShowDemoWindowColumns() } // Create multiple items in a same cell before switching to next column + IMGUI_DEMO_MARKER("Columns (legacy API)/Mixed items"); if (ImGui::TreeNode("Mixed items")) { ImGui::Columns(3, "mixed"); @@ -5344,6 +5558,7 @@ static void ShowDemoWindowColumns() } // Word wrapping + IMGUI_DEMO_MARKER("Columns (legacy API)/Word-wrapping"); if (ImGui::TreeNode("Word-wrapping")) { ImGui::Columns(2, "word-wrapping"); @@ -5358,6 +5573,7 @@ static void ShowDemoWindowColumns() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Columns (legacy API)/Horizontal Scrolling"); if (ImGui::TreeNode("Horizontal Scrolling")) { ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f)); @@ -5383,6 +5599,7 @@ static void ShowDemoWindowColumns() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Columns (legacy API)/Tree"); if (ImGui::TreeNode("Tree")) { ImGui::Columns(2, "tree", true); @@ -5424,6 +5641,7 @@ static void ShowDemoWindowColumns() static void ShowDemoWindowMisc() { + IMGUI_DEMO_MARKER("Filtering"); if (ImGui::CollapsingHeader("Filtering")) { // Helper class to easy setup a text filter. @@ -5441,18 +5659,21 @@ static void ShowDemoWindowMisc() ImGui::BulletText("%s", lines[i]); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus"); if (ImGui::CollapsingHeader("Inputs, Navigation & Focus")) { ImGuiIO& io = ImGui::GetIO(); // Display ImGuiIO output flags ImGui::Text("WantCaptureMouse: %d", io.WantCaptureMouse); + ImGui::Text("WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose); ImGui::Text("WantCaptureKeyboard: %d", io.WantCaptureKeyboard); ImGui::Text("WantTextInput: %d", io.WantTextInput); ImGui::Text("WantSetMousePos: %d", io.WantSetMousePos); ImGui::Text("NavActive: %d, NavVisible: %d", io.NavActive, io.NavVisible); // Display Mouse state + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Mouse State"); if (ImGui::TreeNode("Mouse State")) { if (ImGui::IsMousePosValid()) @@ -5460,16 +5681,18 @@ static void ShowDemoWindowMisc() else ImGui::Text("Mouse pos: "); ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); - ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } - ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse dblclick:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)){ ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + + int count = IM_ARRAYSIZE(io.MouseDown); + ImGui::Text("Mouse down:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } + ImGui::Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d (%d)", i, ImGui::GetMouseClickedCount(i)); } + ImGui::Text("Mouse released:"); for (int i = 0; i < count; i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); ImGui::Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused ImGui::TreePop(); } // Display Keyboard/Mouse state + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Keyboard & Navigation State"); if (ImGui::TreeNode("Keyboard & Navigation State")) { ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyDown(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X) (%.02f secs)", i, i, io.KeysDownDuration[i]); } @@ -5491,6 +5714,7 @@ static void ShowDemoWindowMisc() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Tabbing"); if (ImGui::TreeNode("Tabbing")) { ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields."); @@ -5500,12 +5724,13 @@ static void ShowDemoWindowMisc() ImGui::InputText("3", buf, IM_ARRAYSIZE(buf)); ImGui::PushAllowKeyboardFocus(false); ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf)); - //ImGui::SameLine(); HelpMarker("Use ImGui::PushAllowKeyboardFocus(bool) to disable tabbing through certain widgets."); + ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); ImGui::PopAllowKeyboardFocus(); ImGui::InputText("5", buf, IM_ARRAYSIZE(buf)); ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Focus from code"); if (ImGui::TreeNode("Focus from code")) { bool focus_1 = ImGui::Button("Focus on 1"); ImGui::SameLine(); @@ -5526,6 +5751,7 @@ static void ShowDemoWindowMisc() if (focus_3) ImGui::SetKeyboardFocusHere(); ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf)); if (ImGui::IsItemActive()) has_focus = 3; + ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab."); ImGui::PopAllowKeyboardFocus(); if (has_focus) @@ -5546,6 +5772,7 @@ static void ShowDemoWindowMisc() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Dragging"); if (ImGui::TreeNode("Dragging")) { ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget."); @@ -5574,6 +5801,7 @@ static void ShowDemoWindowMisc() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Mouse cursors"); if (ImGui::TreeNode("Mouse cursors")) { const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "NotAllowed" }; @@ -5611,6 +5839,7 @@ void ImGui::ShowAboutWindow(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Tools/About Dear ImGui"); ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); ImGui::Separator(); ImGui::Text("By Omar Cornut and all Dear ImGui contributors."); @@ -5743,29 +5972,13 @@ void ImGui::ShowAboutWindow(bool* p_open) //----------------------------------------------------------------------------- // [SECTION] Style Editor / ShowStyleEditor() //----------------------------------------------------------------------------- -// - ShowStyleSelector() // - ShowFontSelector() +// - ShowStyleSelector() // - ShowStyleEditor() //----------------------------------------------------------------------------- -// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options. -// Here we use the simplified Combo() api that packs items into a single literal string. -// Useful for quick combo boxes where the choices are known locally. -bool ImGui::ShowStyleSelector(const char* label) -{ - static int style_idx = -1; - if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0")) - { - switch (style_idx) - { - case 0: ImGui::StyleColorsDark(); break; - case 1: ImGui::StyleColorsLight(); break; - case 2: ImGui::StyleColorsClassic(); break; - } - return true; - } - return false; -} +// Forward declare ShowFontAtlas() which isn't worth putting in public API yet +namespace ImGui { IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); } // Demo helper function to select among loaded fonts. // Here we use the regular BeginCombo()/EndCombo() api which is more the more flexible one. @@ -5793,96 +6006,28 @@ void ImGui::ShowFontSelector(const char* label) "- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame()."); } -// [Internal] Display details for a single font, called by ShowStyleEditor(). -static void NodeFont(ImFont* font) +// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options. +// Here we use the simplified Combo() api that packs items into a single literal string. +// Useful for quick combo boxes where the choices are known locally. +bool ImGui::ShowStyleSelector(const char* label) { - ImGuiIO& io = ImGui::GetIO(); - ImGuiStyle& style = ImGui::GetStyle(); - bool font_details_opened = ImGui::TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)", - font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount); - ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) { io.FontDefault = font; } - if (!font_details_opened) - return; - - ImGui::PushFont(font); - ImGui::Text("The quick brown fox jumps over the lazy dog"); - ImGui::PopFont(); - ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font - ImGui::SameLine(); HelpMarker( - "Note than the default embedded font is NOT meant to be scaled.\n\n" - "Font are currently rendered into bitmaps at a given size at the time of building the atlas. " - "You may oversample them to get some flexibility with scaling. " - "You can also render at multiple sizes and select which one to use at runtime.\n\n" - "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)"); - ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); - ImGui::Text("Fallback character: '%c' (U+%04X)", font->FallbackChar, font->FallbackChar); - ImGui::Text("Ellipsis character: '%c' (U+%04X)", font->EllipsisChar, font->EllipsisChar); - const int surface_sqrt = (int)sqrtf((float)font->MetricsTotalSurface); - ImGui::Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt); - for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) - if (font->ConfigData) - if (const ImFontConfig* cfg = &font->ConfigData[config_i]) - ImGui::BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)", - config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y); - if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) + static int style_idx = -1; + if (ImGui::Combo(label, &style_idx, "Dark\0Light\0Classic\0")) { - // Display all glyphs of the fonts in separate pages of 256 characters - const ImU32 glyph_col = ImGui::GetColorU32(ImGuiCol_Text); - for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256) + switch (style_idx) { - // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k) - // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT - // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here) - if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095)) - { - base += 4096 - 256; - continue; - } - - int count = 0; - for (unsigned int n = 0; n < 256; n++) - if (font->FindGlyphNoFallback((ImWchar)(base + n))) - count++; - if (count <= 0) - continue; - if (!ImGui::TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph")) - continue; - float cell_size = font->FontSize * 1; - float cell_spacing = style.ItemSpacing.y; - ImVec2 base_pos = ImGui::GetCursorScreenPos(); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - for (unsigned int n = 0; n < 256; n++) - { - // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions - // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string. - ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); - ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); - const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); - draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); - if (glyph) - font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); - if (glyph && ImGui::IsMouseHoveringRect(cell_p1, cell_p2)) - { - ImGui::BeginTooltip(); - ImGui::Text("Codepoint: U+%04X", base + n); - ImGui::Separator(); - ImGui::Text("Visible: %d", glyph->Visible); - ImGui::Text("AdvanceX: %.1f", glyph->AdvanceX); - ImGui::Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); - ImGui::Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); - ImGui::EndTooltip(); - } - } - ImGui::Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); - ImGui::TreePop(); + case 0: ImGui::StyleColorsDark(); break; + case 1: ImGui::StyleColorsLight(); break; + case 2: ImGui::StyleColorsClassic(); break; } - ImGui::TreePop(); + return true; } - ImGui::TreePop(); + return false; } void ImGui::ShowStyleEditor(ImGuiStyle* ref) { + IMGUI_DEMO_MARKER("Tools/Style Editor"); // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to // (without a reference style pointer, we will use one compared locally as a reference) ImGuiStyle& style = ImGui::GetStyle(); @@ -6037,21 +6182,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGuiIO& io = ImGui::GetIO(); ImFontAtlas* atlas = io.Fonts; HelpMarker("Read FAQ and docs/FONTS.md for details on font loading."); - ImGui::PushItemWidth(120); - for (int i = 0; i < atlas->Fonts.Size; i++) - { - ImFont* font = atlas->Fonts[i]; - ImGui::PushID(font); - NodeFont(font); - ImGui::PopID(); - } - if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) - { - ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); - ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); - ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0, 0), ImVec2(1, 1), tint_col, border_col); - ImGui::TreePop(); - } + ImGui::ShowFontAtlas(atlas); // Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below. // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds). @@ -6063,6 +6194,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) "rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n" "Using those settings here will give you poor quality results."); static float window_scale = 1.0f; + ImGui::PushItemWidth(ImGui::GetFontSize() * 8); if (ImGui::DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window ImGui::SetWindowFontScale(window_scale); ImGui::DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything @@ -6082,7 +6214,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) HelpMarker("Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering)."); ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); - ImGui::PushItemWidth(100); + ImGui::PushItemWidth(ImGui::GetFontSize() * 8); ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f"); if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f; @@ -6129,6 +6261,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) HelpMarker("When drawing circle primitives with \"num_segments == 0\" tesselation will be calculated automatically."); ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero. + ImGui::DragFloat("Disabled Alpha", &style.DisabledAlpha, 0.005f, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); HelpMarker("Additional alpha multiplier for disabled items (multiply over current value of Alpha)."); ImGui::PopItemWidth(); ImGui::EndTabItem(); @@ -6178,6 +6311,7 @@ static void ShowExampleAppMainMenuBar() // (future version will add explicit flags to BeginMenu() to request processing shortcuts) static void ShowExampleMenuFile() { + IMGUI_DEMO_MARKER("Examples/Menu"); ImGui::MenuItem("(demo menu)", NULL, false, false); if (ImGui::MenuItem("New")) {} if (ImGui::MenuItem("Open", "Ctrl+O")) {} @@ -6203,6 +6337,7 @@ static void ShowExampleMenuFile() if (ImGui::MenuItem("Save As..")) {} ImGui::Separator(); + IMGUI_DEMO_MARKER("Examples/Menu/Options"); if (ImGui::BeginMenu("Options")) { static bool enabled = true; @@ -6219,6 +6354,7 @@ static void ShowExampleMenuFile() ImGui::EndMenu(); } + IMGUI_DEMO_MARKER("Examples/Menu/Colors"); if (ImGui::BeginMenu("Colors")) { float sz = ImGui::GetTextLineHeight(); @@ -6239,6 +6375,7 @@ static void ShowExampleMenuFile() // In a real code-base using it would make senses to use this feature from very different code locations. if (ImGui::BeginMenu("Options")) // <-- Append! { + IMGUI_DEMO_MARKER("Examples/Menu/Append to an existing menu"); static bool b = true; ImGui::Checkbox("SomeOption", &b); ImGui::EndMenu(); @@ -6271,6 +6408,7 @@ struct ExampleAppConsole ExampleAppConsole() { + IMGUI_DEMO_MARKER("Examples/Console"); ClearLog(); memset(InputBuf, 0, sizeof(InputBuf)); HistoryPos = -1; @@ -6748,6 +6886,7 @@ static void ShowExampleAppLog(bool* p_open) // Most of the contents of the window will be added by the log.Draw() call. ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver); ImGui::Begin("Example: Log", p_open); + IMGUI_DEMO_MARKER("Examples/Log"); if (ImGui::SmallButton("[Debug] Add 5 entries")) { static int counter = 0; @@ -6778,6 +6917,7 @@ static void ShowExampleAppLayout(bool* p_open) ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver); if (ImGui::Begin("Example: Simple layout", p_open, ImGuiWindowFlags_MenuBar)) { + IMGUI_DEMO_MARKER("Examples/Simple layout"); if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("File")) @@ -6794,6 +6934,7 @@ static void ShowExampleAppLayout(bool* p_open) ImGui::BeginChild("left pane", ImVec2(150, 0), true); for (int i = 0; i < 100; i++) { + // FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav char label[128]; sprintf(label, "MyObject %d", i); if (ImGui::Selectable(label, selected == i)) @@ -6893,6 +7034,7 @@ static void ShowExampleAppPropertyEditor(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Examples/Property Editor"); HelpMarker( "This example shows how you may implement a property editor using two columns.\n" @@ -6928,6 +7070,7 @@ static void ShowExampleAppLongText(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Examples/Long text display"); static int test_type = 0; static ImGuiTextBuffer log; @@ -6989,6 +7132,7 @@ static void ShowExampleAppAutoResize(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Examples/Auto-resizing window"); static int lines = 10; ImGui::TextUnformatted( @@ -7040,6 +7184,7 @@ static void ShowExampleAppConstrainedResize(bool* p_open) ImGuiWindowFlags flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0; if (ImGui::Begin("Example: Constrained Resize", p_open, flags)) { + IMGUI_DEMO_MARKER("Examples/Constrained Resizing window"); if (ImGui::Button("200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine(); if (ImGui::Button("500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine(); if (ImGui::Button("800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); } @@ -7062,12 +7207,12 @@ static void ShowExampleAppConstrainedResize(bool* p_open) // + a context-menu to choose which corner of the screen to use. static void ShowExampleAppSimpleOverlay(bool* p_open) { - const float PAD = 10.0f; static int corner = 0; ImGuiIO& io = ImGui::GetIO(); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; if (corner != -1) { + const float PAD = 10.0f; const ImGuiViewport* viewport = ImGui::GetMainViewport(); ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any! ImVec2 work_size = viewport->WorkSize; @@ -7082,6 +7227,7 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background if (ImGui::Begin("Example: Simple overlay", p_open, window_flags)) { + IMGUI_DEMO_MARKER("Examples/Simple Overlay"); ImGui::Text("Simple overlay\n" "in the corner of the screen.\n" "(right-click to change position)"); ImGui::Separator(); if (ImGui::IsMousePosValid()) @@ -7156,6 +7302,7 @@ static void ShowExampleAppWindowTitles(bool*) // Using "##" to display same title but have unique identifier. ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), ImGuiCond_FirstUseEver); ImGui::Begin("Same title as another window##1"); + IMGUI_DEMO_MARKER("Examples/Manipulating window titles"); ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique."); ImGui::End(); @@ -7185,6 +7332,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) ImGui::End(); return; } + IMGUI_DEMO_MARKER("Examples/Custom Rendering"); // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of // overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your @@ -7567,6 +7715,16 @@ void ShowExampleAppDocuments(bool* p_open) ImGui::Separator(); + // About the ImGuiWindowFlags_UnsavedDocument / ImGuiTabItemFlags_UnsavedDocument flags. + // They have multiple effects: + // - Display a dot next to the title. + // - Tab is selected when clicking the X close button. + // - Closure is not assumed (will wait for user to stop submitting the tab). + // Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. + // We need to assume closure by default otherwise waiting for "lack of submission" on the next frame would leave an empty + // hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window. + // The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole. + // Submit Tab Bar and Tabs { ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0); diff --git a/external/imgui/src/imgui_draw.cpp b/r5dev/thirdparty/imgui/src/imgui_draw.cpp similarity index 95% rename from external/imgui/src/imgui_draw.cpp rename to r5dev/thirdparty/imgui/src/imgui_draw.cpp index 8f851d26..583ae6c0 100644 --- a/external/imgui/src/imgui_draw.cpp +++ b/r5dev/thirdparty/imgui/src/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.83 WIP +// dear imgui, v1.86 // (drawing and font code) /* @@ -26,14 +26,14 @@ Index of this file: #define _CRT_SECURE_NO_WARNINGS #endif -#include "imgui.h" +#include "thirdparty/imgui/include/imgui.h" #ifndef IMGUI_DISABLE #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif -#include "imgui_internal.h" +#include "thirdparty/imgui/include/imgui_internal.h" #ifdef IMGUI_ENABLE_FREETYPE #include "misc/freetype/imgui_freetype.h" #endif @@ -54,9 +54,12 @@ Index of this file: // Visual Studio warnings #ifdef _MSC_VER -#pragma warning (disable: 4127) // condition expression is constant -#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 4127) // condition expression is constant +#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#pragma warning (disable: 6255) // [Static Analyzer] _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead. +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) #endif // Clang/GCC warnings with -Weverything @@ -105,6 +108,9 @@ namespace IMGUI_STB_NAMESPACE #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration +#pragma warning (disable: 6011) // (stb_rectpack) Dereferencing NULL pointer 'cur->next'. +#pragma warning (disable: 6385) // (stb_truetype) Reading invalid data from 'buffer': the readable size is '_Old_3`kernel_width' bytes, but '3' bytes may be read. +#pragma warning (disable: 28182) // (stb_rectpack) Dereferencing NULL pointer. 'cur' contains the same NULL value as 'cur->next' did. #endif #if defined(__clang__) @@ -131,7 +137,7 @@ namespace IMGUI_STB_NAMESPACE #ifdef IMGUI_STB_RECT_PACK_FILENAME #include IMGUI_STB_RECT_PACK_FILENAME #else -#include "imstb_rectpack.h" +#include "thirdparty/imgui/include/imstb_rectpack.h" #endif #endif @@ -155,7 +161,7 @@ namespace IMGUI_STB_NAMESPACE #ifdef IMGUI_STB_TRUETYPE_FILENAME #include IMGUI_STB_TRUETYPE_FILENAME #else -#include "imstb_truetype.h" +#include "thirdparty/imgui/include/imstb_truetype.h" #endif #endif #endif // IMGUI_ENABLE_STB_TRUETYPE @@ -467,6 +473,7 @@ void ImDrawList::_PopUnusedDrawCmd() void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) { + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; IM_ASSERT(curr_cmd->UserCallback == NULL); if (curr_cmd->ElemCount != 0) @@ -485,11 +492,25 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) #define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset #define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset +// Try to merge two last draw commands +void ImDrawList::_TryMergeDrawCmds() +{ + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); + ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; + ImDrawCmd* prev_cmd = curr_cmd - 1; + if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL) + { + prev_cmd->ElemCount += curr_cmd->ElemCount; + CmdBuffer.pop_back(); + } +} + // Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack. // The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only. void ImDrawList::_OnChangedClipRect() { // If current command is used with different settings we need to add a new command + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &_CmdHeader.ClipRect, sizeof(ImVec4)) != 0) { @@ -512,6 +533,7 @@ void ImDrawList::_OnChangedClipRect() void ImDrawList::_OnChangedTextureID() { // If current command is used with different settings we need to add a new command + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; if (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != _CmdHeader.TextureId) { @@ -535,6 +557,7 @@ void ImDrawList::_OnChangedVtxOffset() { // We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this. _VtxCurrentIdx = 0; + IM_ASSERT_PARANOID(CmdBuffer.Size > 0); ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; //IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); // See #3349 if (curr_cmd->ElemCount != 0) @@ -687,10 +710,11 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c } // On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds. -// Those macros expects l-values. -#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0) +// - Those macros expects l-values and need to be used as their own statement. +// - Those macros are intentionally not surrounded by the 'do {} while (0)' idiom because even that translates to runtime with debug compilers. +#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImRsqrt(d2); VX *= inv_len; VY *= inv_len; } } (void)0 #define IM_FIXNORMAL2F_MAX_INVLEN2 100.0f // 500.0f (see #4053, #3366) -#define IM_FIXNORMAL2F(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } while (0) +#define IM_FIXNORMAL2F(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } (void)0 // TODO: Thickness anti-aliased lines cap are missing their AA fringe. // We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds. @@ -1460,24 +1484,22 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu if ((col & IM_COL32_A_MASK) == 0 || radius <= 0.0f) return; - // Obtain segment count if (num_segments <= 0) { - // Automatic segment count - num_segments = _CalcCircleAutoSegmentCount(radius); + // Use arc with automatic segment count + _PathArcToFastEx(center, radius - 0.5f, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0); + _Path.Size--; } else { // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes) num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX); + + // Because we are filling a closed shape we remove 1 from the count of segments/points + const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; + PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1); } - // Because we are filling a closed shape we remove 1 from the count of segments/points - const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; - if (num_segments == 12) - PathArcToFast(center, radius - 0.5f, 0, 12 - 1); - else - PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1); PathStroke(col, ImDrawFlags_Closed, thickness); } @@ -1486,24 +1508,22 @@ void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, if ((col & IM_COL32_A_MASK) == 0 || radius <= 0.0f) return; - // Obtain segment count if (num_segments <= 0) { - // Automatic segment count - num_segments = _CalcCircleAutoSegmentCount(radius); + // Use arc with automatic segment count + _PathArcToFastEx(center, radius, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0); + _Path.Size--; } else { // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes) num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX); + + // Because we are filling a closed shape we remove 1 from the count of segments/points + const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; + PathArcTo(center, radius, 0.0f, a_max, num_segments - 1); } - // Because we are filling a closed shape we remove 1 from the count of segments/points - const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; - if (num_segments == 12) - PathArcToFast(center, radius, 0, 12 - 1); - else - PathArcTo(center, radius, 0.0f, a_max, num_segments - 1); PathFillConvex(col); } @@ -1904,37 +1924,38 @@ ImFontConfig::ImFontConfig() // A work of art lies ahead! (. = white layer, X = black layer, others are blank) // The 2x2 white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes. -const int FONT_ATLAS_DEFAULT_TEX_DATA_W = 108; // Actual texture will be 2 times that + 1 spacing. +// (This is used when io.MouseDrawCursor = true) +const int FONT_ATLAS_DEFAULT_TEX_DATA_W = 122; // Actual texture will be 2 times that + 1 spacing. const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27; static const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] = { - "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX " - "..- -X.....X- X.X - X.X -X.....X - X.....X- X..X " - "--- -XXX.XXX- X...X - X...X -X....X - X....X- X..X " - "X - X.X - X.....X - X.....X -X...X - X...X- X..X " - "XX - X.X -X.......X- X.......X -X..X.X - X.X..X- X..X " - "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X- X..XXX " - "X..X - X.X - X.X - X.X -XX X.X - X.X XX- X..X..XXX " - "X...X - X.X - X.X - XX X.X XX - X.X - X.X - X..X..X..XX " - "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X - X..X..X..X.X " - "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X -XXX X..X..X..X..X" - "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X -X..XX........X..X" - "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X -X...X...........X" - "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X - X..............X" - "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X - X.............X" - "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X - X.............X" - "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X - X............X" - "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX - X...........X " - "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------- X..........X " - "X.X X..X - -X.......X- X.......X - XX XX - - X..........X " - "XX X..X - - X.....X - X.....X - X.X X.X - - X........X " - " X..X - X...X - X...X - X..X X..X - - X........X " - " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - - XXXXXXXXXX " - "------------ - X - X -X.....................X- ------------------" - " ----------------------------------- X...XXXXXXXXXXXXX...X - " - " - X..X X..X - " - " - X.X X.X - " - " - XX XX - " + "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX- XX - XX XX " + "..- -X.....X- X.X - X.X -X.....X - X.....X- X..X -X..X X..X" + "--- -XXX.XXX- X...X - X...X -X....X - X....X- X..X -X...X X...X" + "X - X.X - X.....X - X.....X -X...X - X...X- X..X - X...X X...X " + "XX - X.X -X.......X- X.......X -X..X.X - X.X..X- X..X - X...X...X " + "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X- X..XXX - X.....X " + "X..X - X.X - X.X - X.X -XX X.X - X.X XX- X..X..XXX - X...X " + "X...X - X.X - X.X - XX X.X XX - X.X - X.X - X..X..X..XX - X.X " + "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X - X..X..X..X.X - X...X " + "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X -XXX X..X..X..X..X- X.....X " + "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X -X..XX........X..X- X...X...X " + "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X -X...X...........X- X...X X...X " + "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X - X..............X-X...X X...X" + "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X - X.............X-X..X X..X" + "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X - X.............X- XX XX " + "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X - X............X--------------" + "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX - X...........X - " + "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------- X..........X - " + "X.X X..X - -X.......X- X.......X - XX XX - - X..........X - " + "XX X..X - - X.....X - X.....X - X.X X.X - - X........X - " + " X..X - - X...X - X...X - X..X X..X - - X........X - " + " XX - - X.X - X.X - X...XXXXXXXXXXXXX...X - - XXXXXXXXXX - " + "------------- - X - X -X.....................X- ------------------- " + " ----------------------------------- X...XXXXXXXXXXXXX...X - " + " - X..X X..X - " + " - X.X X.X - " + " - XX XX - " }; static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3] = @@ -1948,6 +1969,7 @@ static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3 { ImVec2(73,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNESW { ImVec2(55,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNWSE { ImVec2(91,0), ImVec2(17,22), ImVec2( 5, 0) }, // ImGuiMouseCursor_Hand + { ImVec2(109,0),ImVec2(13,15), ImVec2( 6, 7) }, // ImGuiMouseCursor_NotAllowed }; ImFontAtlas::ImFontAtlas() @@ -1983,6 +2005,7 @@ void ImFontAtlas::ClearInputData() ConfigData.clear(); CustomRects.clear(); PackIdMouseCursors = PackIdLines = -1; + // Important: we leave TexReady untouched } void ImFontAtlas::ClearTexData() @@ -1995,14 +2018,14 @@ void ImFontAtlas::ClearTexData() TexPixelsAlpha8 = NULL; TexPixelsRGBA32 = NULL; TexPixelsUseColors = false; + // Important: we leave TexReady untouched } void ImFontAtlas::ClearFonts() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - for (int i = 0; i < Fonts.Size; i++) - IM_DELETE(Fonts[i]); - Fonts.clear(); + Fonts.clear_delete(); + TexReady = false; } void ImFontAtlas::Clear() @@ -2016,11 +2039,7 @@ void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_wid { // Build atlas on demand if (TexPixelsAlpha8 == NULL) - { - if (ConfigData.empty()) - AddFontDefault(); Build(); - } *out_pixels = TexPixelsAlpha8; if (out_width) *out_width = TexWidth; @@ -2079,6 +2098,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar; // Invalidate texture + TexReady = false; ClearTexData(); return new_font_cfg.DstFont; } @@ -2150,7 +2170,7 @@ ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float si IM_ASSERT(font_cfg.FontData == NULL); font_cfg.FontData = ttf_data; font_cfg.FontDataSize = ttf_size; - font_cfg.SizePixels = size_pixels; + font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels; if (glyph_ranges) font_cfg.GlyphRanges = glyph_ranges; return AddFont(&font_cfg); @@ -2241,6 +2261,10 @@ bool ImFontAtlas::Build() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); + // Default font is none are specified + if (ConfigData.Size == 0) + AddFontDefault(); + // Select builder // - Note that we do not reassign to atlas->FontBuilderIO, since it is likely to point to static data which // may mess with some hot-reloading schemes. If you need to assign to this (for dynamic selection) AND are @@ -2562,9 +2586,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) } } - // Cleanup temporary (ImVector doesn't honor destructor) - for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) - src_tmp_array[src_i].~ImFontBuildSrcData(); + // Cleanup + src_tmp_array.clear_destruct(); ImFontAtlasBuildFinish(atlas); return true; @@ -2780,22 +2803,7 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas) if (atlas->Fonts[i]->DirtyLookupTables) atlas->Fonts[i]->BuildLookupTable(); - // Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis). - // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character. - // FIXME: Also note that 0x2026 is currently seldom included in our font ranges. Because of this we are more likely to use three individual dots. - for (int i = 0; i < atlas->Fonts.size(); i++) - { - ImFont* font = atlas->Fonts[i]; - if (font->EllipsisChar != (ImWchar)-1) - continue; - const ImWchar ellipsis_variants[] = { (ImWchar)0x2026, (ImWchar)0x0085 }; - for (int j = 0; j < IM_ARRAYSIZE(ellipsis_variants); j++) - if (font->FindGlyphNoFallback(ellipsis_variants[j]) != NULL) // Verify glyph exists - { - font->EllipsisChar = ellipsis_variants[j]; - break; - } - } + atlas->TexReady = true; } // Retrieve list of range (2 int per range, values are inclusive) @@ -2816,6 +2824,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesKorean() 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x3131, 0x3163, // Korean alphabets 0xAC00, 0xD7A3, // Korean characters + 0xFFFD, 0xFFFD, // Invalid 0, }; return &ranges[0]; @@ -2830,6 +2839,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseFull() 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions 0xFF00, 0xFFEF, // Half-width characters + 0xFFFD, 0xFFFD, // Invalid 0x4e00, 0x9FAF, // CJK Ideograms 0, }; @@ -2906,7 +2916,8 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon() 0x2000, 0x206F, // General Punctuation 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF // Half-width characters + 0xFF00, 0xFFEF, // Half-width characters + 0xFFFD, 0xFFFD // Invalid }; static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; if (!full_ranges[0]) @@ -2995,7 +3006,8 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF // Half-width characters + 0xFF00, 0xFFEF, // Half-width characters + 0xFFFD, 0xFFFD // Invalid }; static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 }; if (!full_ranges[0]) @@ -3068,8 +3080,8 @@ void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end) void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges) { for (; ranges[0]; ranges += 2) - for (ImWchar c = ranges[0]; c <= ranges[1]; c++) - AddChar(c); + for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560 + AddChar((ImWchar)c); } void ImFontGlyphRangesBuilder::BuildRanges(ImVector* out_ranges) @@ -3094,8 +3106,9 @@ ImFont::ImFont() { FontSize = 0.0f; FallbackAdvanceX = 0.0f; - FallbackChar = (ImWchar)'?'; + FallbackChar = (ImWchar)-1; EllipsisChar = (ImWchar)-1; + DotChar = (ImWchar)-1; FallbackGlyph = NULL; ContainerAtlas = NULL; ConfigData = NULL; @@ -3126,6 +3139,14 @@ void ImFont::ClearOutputData() MetricsTotalSurface = 0; } +static ImWchar FindFirstExistingGlyph(ImFont* font, const ImWchar* candidate_chars, int candidate_chars_count) +{ + for (int n = 0; n < candidate_chars_count; n++) + if (font->FindGlyphNoFallback(candidate_chars[n]) != NULL) + return candidate_chars[n]; + return (ImWchar)-1; +} + void ImFont::BuildLookupTable() { int max_codepoint = 0; @@ -3168,9 +3189,31 @@ void ImFont::BuildLookupTable() SetGlyphVisible((ImWchar)' ', false); SetGlyphVisible((ImWchar)'\t', false); - // Setup fall-backs + // Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis). + // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character. + // FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots. + const ImWchar ellipsis_chars[] = { (ImWchar)0x2026, (ImWchar)0x0085 }; + const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E }; + if (EllipsisChar == (ImWchar)-1) + EllipsisChar = FindFirstExistingGlyph(this, ellipsis_chars, IM_ARRAYSIZE(ellipsis_chars)); + if (DotChar == (ImWchar)-1) + DotChar = FindFirstExistingGlyph(this, dots_chars, IM_ARRAYSIZE(dots_chars)); + + // Setup fallback character + const ImWchar fallback_chars[] = { (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' }; FallbackGlyph = FindGlyphNoFallback(FallbackChar); - FallbackAdvanceX = FallbackGlyph ? FallbackGlyph->AdvanceX : 0.0f; + if (FallbackGlyph == NULL) + { + FallbackChar = FindFirstExistingGlyph(this, fallback_chars, IM_ARRAYSIZE(fallback_chars)); + FallbackGlyph = FindGlyphNoFallback(FallbackChar); + if (FallbackGlyph == NULL) + { + FallbackGlyph = &Glyphs.back(); + FallbackChar = (ImWchar)FallbackGlyph->Codepoint; + } + } + + FallbackAdvanceX = FallbackGlyph->AdvanceX; for (int i = 0; i < max_codepoint + 1; i++) if (IndexAdvanceX[i] < 0.0f) IndexAdvanceX[i] = FallbackAdvanceX; @@ -3195,12 +3238,6 @@ void ImFont::SetGlyphVisible(ImWchar c, bool visible) glyph->Visible = visible ? 1 : 0; } -void ImFont::SetFallbackChar(ImWchar c) -{ - FallbackChar = c; - BuildLookupTable(); -} - void ImFont::GrowIndex(int new_size) { IM_ASSERT(IndexAdvanceX.Size == IndexLookup.Size); @@ -3702,6 +3739,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col // - RenderMouseCursor() // - RenderArrowPointingAt() // - RenderRectFilledRangeH() +// - RenderRectFilledWithHole() //----------------------------------------------------------------------------- // Function in need of a redesign (legacy mess) // - RenderColorRectWithAlphaCheckerboard() @@ -3770,7 +3808,7 @@ void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, Im if (font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2])) { pos -= offset; - const ImTextureID tex_id = font_atlas->TexID; + ImTextureID tex_id = font_atlas->TexID; draw_list->PushTextureID(tex_id); draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow); draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow); @@ -3868,10 +3906,10 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect const bool fill_R = (inner.Max.x < outer.Max.x); const bool fill_U = (inner.Min.y > outer.Min.y); const bool fill_D = (inner.Max.y < outer.Max.y); - if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft)); - if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight)); - if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight)); - if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight)); + if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft)); + if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight) | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight)); + if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight)); + if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight)); if (fill_L && fill_U) draw_list->AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopLeft); if (fill_R && fill_U) draw_list->AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopRight); if (fill_L && fill_D) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomLeft); diff --git a/external/imgui/src/imgui_impl_dx11.cpp b/r5dev/thirdparty/imgui/src/imgui_impl_dx11.cpp similarity index 72% rename from external/imgui/src/imgui_impl_dx11.cpp rename to r5dev/thirdparty/imgui/src/imgui_impl_dx11.cpp index 869dd4e1..7d616a60 100644 --- a/external/imgui/src/imgui_impl_dx11.cpp +++ b/r5dev/thirdparty/imgui/src/imgui_impl_dx11.cpp @@ -5,12 +5,15 @@ // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). +// 2021-05-19: DirectX11: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-02-18: DirectX11: Change blending equation to preserve alpha in output buffer. // 2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled). // 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore. @@ -26,8 +29,8 @@ // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. // 2016-05-07: DirectX11: Disabling depth-write. -#include "imgui.h" -#include "imgui_impl_dx11.h" +#include "thirdparty/imgui/include/imgui.h" +#include "thirdparty/imgui/include/imgui_impl_dx11.h" // DirectX #include @@ -37,30 +40,46 @@ #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. #endif -// DirectX data -static ID3D11Device* g_pd3dDevice = NULL; -static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; -static IDXGIFactory* g_pFactory = NULL; -static ID3D11Buffer* g_pVB = NULL; -static ID3D11Buffer* g_pIB = NULL; -static ID3D11VertexShader* g_pVertexShader = NULL; -static ID3D11InputLayout* g_pInputLayout = NULL; -static ID3D11Buffer* g_pVertexConstantBuffer = NULL; -static ID3D11PixelShader* g_pPixelShader = NULL; -static ID3D11SamplerState* g_pFontSampler = NULL; -static ID3D11ShaderResourceView*g_pFontTextureView = NULL; -static ID3D11RasterizerState* g_pRasterizerState = NULL; -static ID3D11BlendState* g_pBlendState = NULL; -static ID3D11DepthStencilState* g_pDepthStencilState = NULL; -static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; +// DirectX11 data +struct ImGui_ImplDX11_Data +{ + ID3D11Device* pd3dDevice; + ID3D11DeviceContext* pd3dDeviceContext; + IDXGIFactory* pFactory; + ID3D11Buffer* pVB; + ID3D11Buffer* pIB; + ID3D11VertexShader* pVertexShader; + ID3D11InputLayout* pInputLayout; + ID3D11Buffer* pVertexConstantBuffer; + ID3D11PixelShader* pPixelShader; + ID3D11SamplerState* pFontSampler; + ID3D11ShaderResourceView* pFontTextureView; + ID3D11RasterizerState* pRasterizerState; + ID3D11BlendState* pBlendState; + ID3D11DepthStencilState* pDepthStencilState; + int VertexBufferSize; + int IndexBufferSize; + + ImGui_ImplDX11_Data() { memset(this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; } +}; struct VERTEX_CONSTANT_BUFFER { float mvp[4][4]; }; +// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +static ImGui_ImplDX11_Data* ImGui_ImplDX11_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplDX11_Data*)ImGui::GetIO().BackendRendererUserData : NULL; +} + +// Functions static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx) { + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + // Setup viewport D3D11_VIEWPORT vp; memset(&vp, 0, sizeof(D3D11_VIEWPORT)); @@ -74,14 +93,14 @@ static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceC // Setup shader and vertex buffers unsigned int stride = sizeof(ImDrawVert); unsigned int offset = 0; - ctx->IASetInputLayout(g_pInputLayout); - ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset); - ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); + ctx->IASetInputLayout(bd->pInputLayout); + ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset); + ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - ctx->VSSetShader(g_pVertexShader, NULL, 0); - ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer); - ctx->PSSetShader(g_pPixelShader, NULL, 0); - ctx->PSSetSamplers(0, 1, &g_pFontSampler); + ctx->VSSetShader(bd->pVertexShader, NULL, 0); + ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer); + ctx->PSSetShader(bd->pPixelShader, NULL, 0); + ctx->PSSetSamplers(0, 1, &bd->pFontSampler); ctx->GSSetShader(NULL, NULL, 0); ctx->HSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. ctx->DSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. @@ -89,9 +108,9 @@ static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceC // Setup blend state const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; - ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff); - ctx->OMSetDepthStencilState(g_pDepthStencilState, 0); - ctx->RSSetState(g_pRasterizerState); + ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff); + ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0); + ctx->RSSetState(bd->pRasterizerState); } // Render function @@ -101,42 +120,43 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) return; - ID3D11DeviceContext* ctx = g_pd3dDeviceContext; + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + ID3D11DeviceContext* ctx = bd->pd3dDeviceContext; // Create and grow vertex/index buffers if needed - if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) + if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) { - if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } - g_VertexBufferSize = draw_data->TotalVtxCount + 5000; + if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; } + bd->VertexBufferSize = draw_data->TotalVtxCount + 5000; D3D11_BUFFER_DESC desc; memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); desc.Usage = D3D11_USAGE_DYNAMIC; - desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert); + desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert); desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.MiscFlags = 0; - if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0) + if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVB) < 0) return; } - if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) + if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount) { - if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } - g_IndexBufferSize = draw_data->TotalIdxCount + 10000; + if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; } + bd->IndexBufferSize = draw_data->TotalIdxCount + 10000; D3D11_BUFFER_DESC desc; memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); desc.Usage = D3D11_USAGE_DYNAMIC; - desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx); + desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx); desc.BindFlags = D3D11_BIND_INDEX_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pIB) < 0) + if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pIB) < 0) return; } // Upload vertex/index data into a single contiguous GPU buffer D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource; - if (ctx->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK) + if (ctx->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK) return; - if (ctx->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) + if (ctx->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) return; ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData; ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData; @@ -148,14 +168,14 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) vtx_dst += cmd_list->VtxBuffer.Size; idx_dst += cmd_list->IdxBuffer.Size; } - ctx->Unmap(g_pVB, 0); - ctx->Unmap(g_pIB, 0); + ctx->Unmap(bd->pVB, 0); + ctx->Unmap(bd->pIB, 0); // Setup orthographic projection matrix into our constant buffer // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. { D3D11_MAPPED_SUBRESOURCE mapped_resource; - if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) + if (ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) return; VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData; float L = draw_data->DisplayPos.x; @@ -170,7 +190,7 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, }; memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); - ctx->Unmap(g_pVertexConstantBuffer, 0); + ctx->Unmap(bd->pVertexConstantBuffer, 0); } // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!) @@ -243,12 +263,18 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) } else { + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); + ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; + // Apply scissor/clipping rectangle - const D3D11_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) }; + const D3D11_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y }; ctx->RSSetScissorRects(1, &r); // Bind texture, Draw - ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->TextureId; + ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->GetTexID(); ctx->PSSetShaderResources(0, 1, &texture_srv); ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset); } @@ -281,6 +307,7 @@ static void ImGui_ImplDX11_CreateFontsTexture() { // Build texture atlas ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); @@ -304,7 +331,8 @@ static void ImGui_ImplDX11_CreateFontsTexture() subResource.pSysMem = pixels; subResource.SysMemPitch = desc.Width * 4; subResource.SysMemSlicePitch = 0; - g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); + bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); + IM_ASSERT(pTexture != NULL); // Create texture view D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; @@ -313,12 +341,12 @@ static void ImGui_ImplDX11_CreateFontsTexture() srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = desc.MipLevels; srvDesc.Texture2D.MostDetailedMip = 0; - g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView); + bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &bd->pFontTextureView); pTexture->Release(); } // Store our identifier - io.Fonts->SetTexID((ImTextureID)g_pFontTextureView); + io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView); // Create texture sampler { @@ -332,15 +360,16 @@ static void ImGui_ImplDX11_CreateFontsTexture() desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; desc.MinLOD = 0.f; desc.MaxLOD = 0.f; - g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler); + bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler); } } bool ImGui_ImplDX11_CreateDeviceObjects() { - if (!g_pd3dDevice) + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + if (!bd->pd3dDevice) return false; - if (g_pFontSampler) + if (bd->pFontSampler) ImGui_ImplDX11_InvalidateDeviceObjects(); // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) @@ -375,7 +404,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() {\ PS_INPUT output;\ output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\ - output.col.xyz = pow(abs(input.col.xyz), 2.2f);\ + output.col.xyz = pow(abs(input.col.xyz), 2.2f);\ output.col.w = input.col.w;\ output.uv = input.uv;\ return output;\ @@ -384,7 +413,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() ID3DBlob* vertexShaderBlob; if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vertexShaderBlob, NULL))) return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - if (g_pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), NULL, &g_pVertexShader) != S_OK) + if (bd->pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), NULL, &bd->pVertexShader) != S_OK) { vertexShaderBlob->Release(); return false; @@ -397,7 +426,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; - if (g_pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK) + if (bd->pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pInputLayout) != S_OK) { vertexShaderBlob->Release(); return false; @@ -412,7 +441,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.MiscFlags = 0; - g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer); + bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVertexConstantBuffer); } } @@ -437,7 +466,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() ID3DBlob* pixelShaderBlob; if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &pixelShaderBlob, NULL))) return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - if (g_pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), NULL, &g_pPixelShader) != S_OK) + if (bd->pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), NULL, &bd->pPixelShader) != S_OK) { pixelShaderBlob->Release(); return false; @@ -458,7 +487,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; - g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState); + bd->pd3dDevice->CreateBlendState(&desc, &bd->pBlendState); } // Create the rasterizer state @@ -469,7 +498,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() desc.CullMode = D3D11_CULL_NONE; desc.ScissorEnable = true; desc.DepthClipEnable = true; - g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState); + bd->pd3dDevice->CreateRasterizerState(&desc, &bd->pRasterizerState); } // Create depth-stencil State @@ -483,7 +512,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; desc.BackFace = desc.FrontFace; - g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState); + bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState); } ImGui_ImplDX11_CreateFontsTexture(); @@ -493,27 +522,31 @@ bool ImGui_ImplDX11_CreateDeviceObjects() void ImGui_ImplDX11_InvalidateDeviceObjects() { - if (!g_pd3dDevice) + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + if (!bd->pd3dDevice) return; - if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; } - if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. - if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } - if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } - - if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; } - if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; } - if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; } - if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; } - if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; } - if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; } - if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; } + if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = NULL; } + if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied data->pFontTextureView to io.Fonts->TexID so let's clear that as well. + if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; } + if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; } + if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = NULL; } + if (bd->pDepthStencilState) { bd->pDepthStencilState->Release(); bd->pDepthStencilState = NULL; } + if (bd->pRasterizerState) { bd->pRasterizerState->Release(); bd->pRasterizerState = NULL; } + if (bd->pPixelShader) { bd->pPixelShader->Release(); bd->pPixelShader = NULL; } + if (bd->pVertexConstantBuffer) { bd->pVertexConstantBuffer->Release(); bd->pVertexConstantBuffer = NULL; } + if (bd->pInputLayout) { bd->pInputLayout->Release(); bd->pInputLayout = NULL; } + if (bd->pVertexShader) { bd->pVertexShader->Release(); bd->pVertexShader = NULL; } } bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context) { - // Setup backend capabilities flags ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + ImGui_ImplDX11_Data* bd = IM_NEW(ImGui_ImplDX11_Data)(); + io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx11"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. @@ -526,28 +559,38 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK) if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK) { - g_pd3dDevice = device; - g_pd3dDeviceContext = device_context; - g_pFactory = pFactory; + bd->pd3dDevice = device; + bd->pd3dDeviceContext = device_context; + bd->pFactory = pFactory; } if (pDXGIDevice) pDXGIDevice->Release(); if (pDXGIAdapter) pDXGIAdapter->Release(); - g_pd3dDevice->AddRef(); - g_pd3dDeviceContext->AddRef(); + bd->pd3dDevice->AddRef(); + bd->pd3dDeviceContext->AddRef(); return true; } void ImGui_ImplDX11_Shutdown() { + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX11_InvalidateDeviceObjects(); - if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; } - if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } - if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; } + if (bd->pFactory) { bd->pFactory->Release(); } + if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } + if (bd->pd3dDeviceContext) { bd->pd3dDeviceContext->Release(); } + io.BackendRendererName = NULL; + io.BackendRendererUserData = NULL; + IM_DELETE(bd); } void ImGui_ImplDX11_NewFrame() { - if (!g_pFontSampler) + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + IM_ASSERT(bd != NULL && "Did you call ImGui_ImplDX11_Init()?"); + + if (!bd->pFontSampler) ImGui_ImplDX11_CreateDeviceObjects(); } diff --git a/external/imgui/src/imgui_impl_win32.cpp b/r5dev/thirdparty/imgui/src/imgui_impl_win32.cpp similarity index 67% rename from external/imgui/src/imgui_impl_win32.cpp rename to r5dev/thirdparty/imgui/src/imgui_impl_win32.cpp index edb7c3c5..3573797f 100644 --- a/external/imgui/src/imgui_impl_win32.cpp +++ b/r5dev/thirdparty/imgui/src/imgui_impl_win32.cpp @@ -7,12 +7,13 @@ // [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE). // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. // Read online: https://github.com/ocornut/imgui/tree/master/docs -#include "imgui.h" -#include "imgui_impl_win32.h" +#include "thirdparty/imgui/include/imgui.h" +#include "thirdparty/imgui/include/imgui_impl_win32.h" #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif @@ -21,17 +22,23 @@ #include // Configuration flags to add in your imconfig.h file: -//#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support (this used to be meaningful before <1.81) but we know load XInput dynamically so the option is less relevant now. +//#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support. This was meaningful before <1.81 but we now load XInput dynamically so the option is now less relevant. // Using XInput for gamepad (will load DLL dynamically) #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD -#include +#include typedef DWORD (WINAPI *PFN_XInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*); typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); #endif // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-12-16: Inputs: Fill VK_LCONTROL/VK_RCONTROL/VK_LSHIFT/VK_RSHIFT/VK_LMENU/VK_RMENU for completeness. +// 2021-08-17: Calling io.AddFocusEvent() on WM_SETFOCUS/WM_KILLFOCUS messages. +// 2021-08-02: Inputs: Fixed keyboard modifiers being reported when host window doesn't have focus. +// 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using TrackMouseEvent() to receive WM_MOUSELEAVE events). +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). +// 2021-06-08: Fixed ImGui_ImplWin32_EnableDpiAwareness() and ImGui_ImplWin32_GetDpiScaleForMonitor() to handle Windows 8.1/10 features without a manifest (per-monitor DPI, and properly calls SetProcessDpiAwareness() on 8.1). // 2021-03-23: Inputs: Clearing keyboard down array when losing focus (WM_KILLFOCUS). // 2021-02-18: Added ImGui_ImplWin32_EnableAlphaCompositing(). Non Visual Studio users will need to link with dwmapi.lib (MinGW/gcc: use -ldwmapi). // 2021-02-17: Fixed ImGui_ImplWin32_EnableDpiAwareness() attempting to get SetProcessDpiAwareness from shcore.dll on Windows 8 whereas it is only supported on Windows 8.1. @@ -61,35 +68,60 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging. // 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set. -// Win32 Data -static HWND g_hWnd = NULL; -static INT64 g_Time = 0; -static INT64 g_TicksPerSecond = 0; -static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT; -static bool g_HasGamepad = false; -static bool g_WantUpdateHasGamepad = true; +struct ImGui_ImplWin32_Data +{ + HWND hWnd; + HWND MouseHwnd; + bool MouseTracked; + INT64 Time; + INT64 TicksPerSecond; + ImGuiMouseCursor LastMouseCursor; + bool HasGamepad; + bool WantUpdateHasGamepad; -// XInput DLL and functions #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD -static HMODULE g_XInputDLL = NULL; -static PFN_XInputGetCapabilities g_XInputGetCapabilities = NULL; -static PFN_XInputGetState g_XInputGetState = NULL; + HMODULE XInputDLL; + PFN_XInputGetCapabilities XInputGetCapabilities; + PFN_XInputGetState XInputGetState; #endif + ImGui_ImplWin32_Data() { memset(this, 0, sizeof(*this)); } +}; + +// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +// FIXME: multi-context support is not well tested and probably dysfunctional in this backend. +// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. +static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplWin32_Data*)ImGui::GetIO().BackendPlatformUserData : NULL; +} + // Functions bool ImGui_ImplWin32_Init(void* hwnd) { - if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&g_TicksPerSecond)) + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); + + INT64 perf_frequency, perf_counter; + if (!::QueryPerformanceFrequency((LARGE_INTEGER*)&perf_frequency)) return false; - if (!::QueryPerformanceCounter((LARGE_INTEGER*)&g_Time)) + if (!::QueryPerformanceCounter((LARGE_INTEGER*)&perf_counter)) return false; // Setup backend capabilities flags - g_hWnd = (HWND)hwnd; - ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplWin32_Data* bd = IM_NEW(ImGui_ImplWin32_Data)(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_win32"; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - io.BackendPlatformName = "imgui_impl_win32"; + + bd->hWnd = (HWND)hwnd; + bd->WantUpdateHasGamepad = true; + bd->TicksPerSecond = perf_frequency; + bd->Time = perf_counter; + bd->LastMouseCursor = ImGuiMouseCursor_COUNT; + io.ImeWindowHandle = hwnd; // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. @@ -129,9 +161,9 @@ bool ImGui_ImplWin32_Init(void* hwnd) for (int n = 0; n < IM_ARRAYSIZE(xinput_dll_names); n++) if (HMODULE dll = ::LoadLibraryA(xinput_dll_names[n])) { - g_XInputDLL = dll; - g_XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities"); - g_XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState"); + bd->XInputDLL = dll; + bd->XInputGetCapabilities = (PFN_XInputGetCapabilities)::GetProcAddress(dll, "XInputGetCapabilities"); + bd->XInputGetState = (PFN_XInputGetState)::GetProcAddress(dll, "XInputGetState"); break; } #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD @@ -141,21 +173,19 @@ bool ImGui_ImplWin32_Init(void* hwnd) void ImGui_ImplWin32_Shutdown() { + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + // Unload XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - if (g_XInputDLL) - ::FreeLibrary(g_XInputDLL); - g_XInputDLL = NULL; - g_XInputGetCapabilities = NULL; - g_XInputGetState = NULL; + if (bd->XInputDLL) + ::FreeLibrary(bd->XInputDLL); #endif // IMGUI_IMPL_WIN32_DISABLE_GAMEPAD - g_hWnd = NULL; - g_Time = 0; - g_TicksPerSecond = 0; - g_LastMouseCursor = ImGuiMouseCursor_COUNT; - g_HasGamepad = false; - g_WantUpdateHasGamepad = true; + io.BackendPlatformName = NULL; + io.BackendPlatformUserData = NULL; + IM_DELETE(bd); } static bool ImGui_ImplWin32_UpdateMouseCursor() @@ -193,24 +223,36 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() static void ImGui_ImplWin32_UpdateMousePos() { + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(g_hWnd != 0); + IM_ASSERT(bd->hWnd != 0); - // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + const ImVec2 mouse_pos_prev = io.MousePos; + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + + // Obtain focused and hovered window. We forward mouse input when focused or when hovered (and no other window is capturing) + HWND focused_window = ::GetForegroundWindow(); + HWND hovered_window = bd->MouseHwnd; + HWND mouse_window = NULL; + if (hovered_window && (hovered_window == bd->hWnd || ::IsChild(hovered_window, bd->hWnd))) + mouse_window = hovered_window; + else if (focused_window && (focused_window == bd->hWnd || ::IsChild(focused_window, bd->hWnd))) + mouse_window = focused_window; + if (mouse_window == NULL) + return; + + // Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) if (io.WantSetMousePos) { - POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - if (::ClientToScreen(g_hWnd, &pos)) + POINT pos = { (int)mouse_pos_prev.x, (int)mouse_pos_prev.y }; + if (::ClientToScreen(bd->hWnd, &pos)) ::SetCursorPos(pos.x, pos.y); } - // Set mouse position - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + // Set Dear ImGui mouse position from OS position POINT pos; - if (HWND active_window = ::GetForegroundWindow()) - if (active_window == g_hWnd || ::IsChild(active_window, g_hWnd)) - if (::GetCursorPos(&pos) && ::ScreenToClient(g_hWnd, &pos)) - io.MousePos = ImVec2((float)pos.x, (float)pos.y); + if (::GetCursorPos(&pos) && ::ScreenToClient(mouse_window, &pos)) + io.MousePos = ImVec2((float)pos.x, (float)pos.y); } // Gamepad navigation mapping @@ -218,22 +260,23 @@ static void ImGui_ImplWin32_UpdateGamepads() { #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); memset(io.NavInputs, 0, sizeof(io.NavInputs)); if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) return; // Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow. // Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE. - if (g_WantUpdateHasGamepad) + if (bd->WantUpdateHasGamepad) { XINPUT_CAPABILITIES caps; - g_HasGamepad = g_XInputGetCapabilities ? (g_XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false; - g_WantUpdateHasGamepad = false; + bd->HasGamepad = bd->XInputGetCapabilities ? (bd->XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) : false; + bd->WantUpdateHasGamepad = false; } io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; XINPUT_STATE xinput_state; - if (g_HasGamepad && g_XInputGetState && g_XInputGetState(0, &xinput_state) == ERROR_SUCCESS) + if (bd->HasGamepad && bd->XInputGetState && bd->XInputGetState(0, &xinput_state) == ERROR_SUCCESS) { const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad; io.BackendFlags |= ImGuiBackendFlags_HasGamepad; @@ -265,34 +308,28 @@ static void ImGui_ImplWin32_UpdateGamepads() void ImGui_ImplWin32_NewFrame() { ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + IM_ASSERT(bd != NULL && "Did you call ImGui_ImplWin32_Init()?"); // Setup display size (every frame to accommodate for window resizing) RECT rect = { 0, 0, 0, 0 }; - ::GetClientRect(g_hWnd, &rect); + ::GetClientRect(bd->hWnd, &rect); io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); // Setup time step INT64 current_time = 0; ::QueryPerformanceCounter((LARGE_INTEGER*)¤t_time); - io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; - g_Time = current_time; - - // Read keyboard modifiers inputs - io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0; - io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0; - io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0; - io.KeySuper = false; - // io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below. + io.DeltaTime = (float)(current_time - bd->Time) / bd->TicksPerSecond; + bd->Time = current_time; // Update OS mouse position ImGui_ImplWin32_UpdateMousePos(); // Update OS mouse cursor with the cursor requested by imgui ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); - if (g_LastMouseCursor != mouse_cursor) + if (bd->LastMouseCursor != mouse_cursor) { - g_LastMouseCursor = mouse_cursor; + bd->LastMouseCursor = mouse_cursor; ImGui_ImplWin32_UpdateMouseCursor(); } @@ -326,8 +363,25 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA return 0; ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + switch (msg) { + case WM_MOUSEMOVE: + // We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events + bd->MouseHwnd = hwnd; + if (!bd->MouseTracked) + { + TRACKMOUSEEVENT tme = { sizeof(tme), TME_LEAVE, hwnd, 0 }; + ::TrackMouseEvent(&tme); + bd->MouseTracked = true; + } + break; + case WM_MOUSELEAVE: + if (bd->MouseHwnd == hwnd) + bd->MouseHwnd = NULL; + bd->MouseTracked = false; + break; case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: @@ -365,17 +419,36 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; return 0; case WM_KEYDOWN: - case WM_SYSKEYDOWN: - if (wParam < 256) - io.KeysDown[wParam] = 1; - return 0; case WM_KEYUP: + case WM_SYSKEYDOWN: case WM_SYSKEYUP: + { + bool down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN); if (wParam < 256) - io.KeysDown[wParam] = 0; + io.KeysDown[wParam] = down; + if (wParam == VK_CONTROL) + { + io.KeysDown[VK_LCONTROL] = ((::GetKeyState(VK_LCONTROL) & 0x8000) != 0); + io.KeysDown[VK_RCONTROL] = ((::GetKeyState(VK_RCONTROL) & 0x8000) != 0); + io.KeyCtrl = io.KeysDown[VK_LCONTROL] || io.KeysDown[VK_RCONTROL]; + } + if (wParam == VK_SHIFT) + { + io.KeysDown[VK_LSHIFT] = ((::GetKeyState(VK_LSHIFT) & 0x8000) != 0); + io.KeysDown[VK_RSHIFT] = ((::GetKeyState(VK_RSHIFT) & 0x8000) != 0); + io.KeyShift = io.KeysDown[VK_LSHIFT] || io.KeysDown[VK_RSHIFT]; + } + if (wParam == VK_MENU) + { + io.KeysDown[VK_LMENU] = ((::GetKeyState(VK_LMENU) & 0x8000) != 0); + io.KeysDown[VK_RMENU] = ((::GetKeyState(VK_RMENU) & 0x8000) != 0); + io.KeyAlt = io.KeysDown[VK_LMENU] || io.KeysDown[VK_RMENU]; + } return 0; + } + case WM_SETFOCUS: case WM_KILLFOCUS: - memset(io.KeysDown, 0, sizeof(io.KeysDown)); + io.AddFocusEvent(msg == WM_SETFOCUS); return 0; case WM_CHAR: // You can also use ToAscii()+GetKeyboardState() to retrieve characters. @@ -388,7 +461,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA return 0; case WM_DEVICECHANGE: if ((UINT)wParam == DBT_DEVNODES_CHANGED) - g_WantUpdateHasGamepad = true; + bd->WantUpdateHasGamepad = true; return 0; } return 0; @@ -409,21 +482,32 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA // If you are trying to implement your own backend for your own engine, you may ignore that noise. //--------------------------------------------------------------------------------------------------------- -// Implement some of the functions and types normally declared in recent Windows SDK. -#if !defined(_versionhelpers_H_INCLUDED_) && !defined(_INC_VERSIONHELPERS) -static BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) +// Perform our own check with RtlVerifyVersionInfo() instead of using functions from as they +// require a manifest to be functional for checks above 8.1. See https://github.com/ocornut/imgui/issues/4200 +static BOOL _IsWindowsVersionOrGreater(WORD major, WORD minor, WORD) { - OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, { 0 }, sp, 0, 0, 0, 0 }; - DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; - ULONGLONG cond = ::VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); - cond = ::VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); - cond = ::VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); - return ::VerifyVersionInfoW(&osvi, mask, cond); + typedef LONG(WINAPI* PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*, ULONG, ULONGLONG); + static PFN_RtlVerifyVersionInfo RtlVerifyVersionInfoFn = NULL; + if (RtlVerifyVersionInfoFn == NULL) + if (HMODULE ntdllModule = ::GetModuleHandleA("ntdll.dll")) + RtlVerifyVersionInfoFn = (PFN_RtlVerifyVersionInfo)GetProcAddress(ntdllModule, "RtlVerifyVersionInfo"); + if (RtlVerifyVersionInfoFn == NULL) + return FALSE; + + RTL_OSVERSIONINFOEXW versionInfo = { }; + ULONGLONG conditionMask = 0; + versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + versionInfo.dwMajorVersion = major; + versionInfo.dwMinorVersion = minor; + VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); + return (RtlVerifyVersionInfoFn(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask) == 0) ? TRUE : FALSE; } -#define IsWindowsVistaOrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0600), LOBYTE(0x0600), 0) // _WIN32_WINNT_VISTA -#define IsWindows8OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WIN8 -#define IsWindows8Point1OrGreater() IsWindowsVersionOrGreater(HIBYTE(0x0603), LOBYTE(0x0603), 0) // _WIN32_WINNT_WINBLUE -#endif + +#define _IsWindowsVistaOrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0600), LOBYTE(0x0600), 0) // _WIN32_WINNT_VISTA +#define _IsWindows8OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0602), LOBYTE(0x0602), 0) // _WIN32_WINNT_WIN8 +#define _IsWindows8Point1OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0603), LOBYTE(0x0603), 0) // _WIN32_WINNT_WINBLUE +#define _IsWindows10OrGreater() _IsWindowsVersionOrGreater(HIBYTE(0x0A00), LOBYTE(0x0A00), 0) // _WIN32_WINNT_WINTHRESHOLD / _WIN32_WINNT_WIN10 #ifndef DPI_ENUMS_DECLARED typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; @@ -443,7 +527,7 @@ typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWAR // Helper function to enable DPI awareness without setting up a manifest void ImGui_ImplWin32_EnableDpiAwareness() { - // if (IsWindows10OrGreater()) // This needs a manifest to succeed. Instead we try to grab the function pointer! + if (_IsWindows10OrGreater()) { static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext")) @@ -452,7 +536,7 @@ void ImGui_ImplWin32_EnableDpiAwareness() return; } } - if (IsWindows8Point1OrGreater()) + if (_IsWindows8Point1OrGreater()) { static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process if (PFN_SetProcessDpiAwareness SetProcessDpiAwarenessFn = (PFN_SetProcessDpiAwareness)::GetProcAddress(shcore_dll, "SetProcessDpiAwareness")) @@ -473,23 +557,26 @@ void ImGui_ImplWin32_EnableDpiAwareness() float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor) { UINT xdpi = 96, ydpi = 96; - static BOOL bIsWindows8Point1OrGreater = IsWindows8Point1OrGreater(); - if (bIsWindows8Point1OrGreater) + if (_IsWindows8Point1OrGreater()) { - static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process - if (PFN_GetDpiForMonitor GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor")) - GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); + static HINSTANCE shcore_dll = ::LoadLibraryA("shcore.dll"); // Reference counted per-process + static PFN_GetDpiForMonitor GetDpiForMonitorFn = NULL; + if (GetDpiForMonitorFn == NULL && shcore_dll != NULL) + GetDpiForMonitorFn = (PFN_GetDpiForMonitor)::GetProcAddress(shcore_dll, "GetDpiForMonitor"); + if (GetDpiForMonitorFn != NULL) + { + GetDpiForMonitorFn((HMONITOR)monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); + IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert! + return xdpi / 96.0f; + } } #ifndef NOGDI - else - { - const HDC dc = ::GetDC(NULL); - xdpi = ::GetDeviceCaps(dc, LOGPIXELSX); - ydpi = ::GetDeviceCaps(dc, LOGPIXELSY); - ::ReleaseDC(NULL, dc); - } -#endif + const HDC dc = ::GetDC(NULL); + xdpi = ::GetDeviceCaps(dc, LOGPIXELSX); + ydpi = ::GetDeviceCaps(dc, LOGPIXELSY); IM_ASSERT(xdpi == ydpi); // Please contact me if you hit this assert! + ::ReleaseDC(NULL, dc); +#endif return xdpi / 96.0f; } @@ -512,7 +599,7 @@ float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd) // (the Dwm* functions are Vista era functions but we are borrowing logic from GLFW) void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd) { - if (!IsWindowsVistaOrGreater()) + if (!_IsWindowsVistaOrGreater()) return; BOOL composition; @@ -521,7 +608,7 @@ void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd) BOOL opaque; DWORD color; - if (IsWindows8OrGreater() || (SUCCEEDED(::DwmGetColorizationColor(&color, &opaque)) && !opaque)) + if (_IsWindows8OrGreater() || (SUCCEEDED(::DwmGetColorizationColor(&color, &opaque)) && !opaque)) { HRGN region = ::CreateRectRgn(0, 0, -1, -1); DWM_BLURBEHIND bb = {}; diff --git a/external/imgui/src/imgui_stdlib.cpp b/r5dev/thirdparty/imgui/src/imgui_stdlib.cpp similarity index 97% rename from external/imgui/src/imgui_stdlib.cpp rename to r5dev/thirdparty/imgui/src/imgui_stdlib.cpp index cb1fe174..c3834c06 100644 --- a/external/imgui/src/imgui_stdlib.cpp +++ b/r5dev/thirdparty/imgui/src/imgui_stdlib.cpp @@ -8,8 +8,8 @@ // Changelog: // - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string -#include "imgui.h" -#include "imgui_stdlib.h" +#include "thirdparty/imgui/include/imgui.h" +#include "thirdparty/imgui/include/imgui_stdlib.h" struct InputTextCallback_UserData { diff --git a/external/imgui/src/imgui_tables.cpp b/r5dev/thirdparty/imgui/src/imgui_tables.cpp similarity index 95% rename from external/imgui/src/imgui_tables.cpp rename to r5dev/thirdparty/imgui/src/imgui_tables.cpp index 3677f9e4..1161d1fe 100644 --- a/external/imgui/src/imgui_tables.cpp +++ b/r5dev/thirdparty/imgui/src/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.83 WIP +// dear imgui, v1.86 // (tables and columns code) /* @@ -188,13 +188,13 @@ Index of this file: #define _CRT_SECURE_NO_WARNINGS #endif -#include "imgui.h" +#include "thirdparty/imgui/include/imgui.h" #ifndef IMGUI_DISABLE #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif -#include "imgui_internal.h" +#include "thirdparty/imgui/include/imgui_internal.h" // System includes #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier @@ -210,6 +210,8 @@ Index of this file: #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything @@ -286,12 +288,7 @@ inline ImGuiTableFlags TableFixFlags(ImGuiTableFlags flags, ImGuiWindow* outer_w flags |= ImGuiTableFlags_NoSavedSettings; // Inherit _NoSavedSettings from top-level window (child windows always have _NoSavedSettings set) -#ifdef IMGUI_HAS_DOCK - ImGuiWindow* window_for_settings = outer_window->RootWindowDockStop; -#else - ImGuiWindow* window_for_settings = outer_window->RootWindow; -#endif - if (window_for_settings->Flags & ImGuiWindowFlags_NoSavedSettings) + if (outer_window->RootWindow->Flags & ImGuiWindowFlags_NoSavedSettings) flags |= ImGuiTableFlags_NoSavedSettings; return flags; @@ -327,7 +324,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG const ImVec2 avail_size = GetContentRegionAvail(); ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f); ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size); - if (use_child_window && IsClippedEx(outer_rect, 0, false)) + if (use_child_window && IsClippedEx(outer_rect, 0)) { ItemSize(outer_rect); return false; @@ -341,6 +338,15 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG if (instance_no > 0) IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID"); + // Acquire temporary buffers + const int table_idx = g.Tables.GetIndex(table); + if (++g.TablesTempDataStacked > g.TablesTempData.Size) + g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData()); + ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1]; + temp_data->TableIndex = table_idx; + table->DrawSplitter = &table->TempData->DrawSplitter; + table->DrawSplitter->Clear(); + // Fix flags table->IsDefaultSizingPolicy = (flags & ImGuiTableFlags_SizingMask_) == 0; flags = TableFixFlags(flags, outer_window); @@ -354,7 +360,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->ColumnsCount = columns_count; table->IsLayoutLocked = false; table->InnerWidth = inner_width; - table->UserOuterSize = outer_size; + temp_data->UserOuterSize = outer_size; // When not using a child window, WorkRect.Max will grow as we append contents. if (use_child_window) @@ -403,14 +409,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->HostIndentX = inner_window->DC.Indent.x; table->HostClipRect = inner_window->ClipRect; table->HostSkipItems = inner_window->SkipItems; - table->HostBackupWorkRect = inner_window->WorkRect; - table->HostBackupParentWorkRect = inner_window->ParentWorkRect; - table->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset; - table->HostBackupPrevLineSize = inner_window->DC.PrevLineSize; - table->HostBackupCurrLineSize = inner_window->DC.CurrLineSize; - table->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos; - table->HostBackupItemWidth = outer_window->DC.ItemWidth; - table->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; + temp_data->HostBackupWorkRect = inner_window->WorkRect; + temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect; + temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset; + temp_data->HostBackupPrevLineSize = inner_window->DC.PrevLineSize; + temp_data->HostBackupCurrLineSize = inner_window->DC.CurrLineSize; + temp_data->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos; + temp_data->HostBackupItemWidth = outer_window->DC.ItemWidth; + temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); // Padding and Spacing @@ -453,8 +459,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight); // Make table current - const int table_idx = g.Tables.GetIndex(table); - g.CurrentTableStack.push_back(ImGuiPtrOrIndex(table_idx)); g.CurrentTable = table; outer_window->DC.CurrentTableIdx = table_idx; if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly. @@ -467,6 +471,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG if (table_idx >= g.TablesLastTimeActive.Size) g.TablesLastTimeActive.resize(table_idx + 1, -1.0f); g.TablesLastTimeActive[table_idx] = (float)g.Time; + temp_data->LastTimeActive = (float)g.Time; table->MemoryCompacted = false; // Setup memory buffer (clear data if columns count changed) @@ -511,7 +516,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG *column = ImGuiTableColumn(); column->WidthAuto = width_auto; column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker - column->IsEnabled = column->IsEnabledNextFrame = true; + column->IsEnabled = column->IsUserEnabled = column->IsUserEnabledNextFrame = true; } column->DisplayOrder = table->DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)n; } @@ -558,6 +563,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables) // + 1 (for table->RawData allocated below) // + 1 (for table->ColumnsNames, if names are used) +// Shared allocations per number of nested tables // + 1 (for table->Splitter._Channels) // + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels) // Where active_channels_count is variable but often == columns_count or columns_count + 1, see TableSetupDrawChannels() for details. @@ -745,16 +751,18 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) column->InitStretchWeightOrWidth = -1.0f; } - // Update Enabled state, mark settings/sortspecs dirty + // Update Enabled state, mark settings and sort specs dirty if (!(table->Flags & ImGuiTableFlags_Hideable) || (column->Flags & ImGuiTableColumnFlags_NoHide)) - column->IsEnabledNextFrame = true; - if (column->IsEnabled != column->IsEnabledNextFrame) + column->IsUserEnabledNextFrame = true; + if (column->IsUserEnabled != column->IsUserEnabledNextFrame) { - column->IsEnabled = column->IsEnabledNextFrame; + column->IsUserEnabled = column->IsUserEnabledNextFrame; table->IsSettingsDirty = true; - if (!column->IsEnabled && column->SortOrder != -1) - table->IsSortSpecsDirty = true; } + column->IsEnabled = column->IsUserEnabled && (column->Flags & ImGuiTableColumnFlags_Disabled) == 0; + + if (column->SortOrder != -1 && !column->IsEnabled) + table->IsSortSpecsDirty = true; if (column->SortOrder > 0 && !(table->Flags & ImGuiTableFlags_SortMulti)) table->IsSortSpecsDirty = true; @@ -1115,7 +1123,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // Initial state ImGuiWindow* inner_window = table->InnerWindow; if (table->Flags & ImGuiTableFlags_NoClip) - table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); + table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); else inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false); } @@ -1153,9 +1161,8 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) if ((table->Flags & ImGuiTableFlags_NoBordersInBody) && table->IsUsingHeaders == false) continue; - if (table->FreezeColumnsCount > 0) - if (column->MaxX < table->Columns[table->DisplayOrderToIndex[table->FreezeColumnsCount - 1]].MaxX) - continue; + if (!column->IsVisibleX && table->LastResizedColumn != column_n) + continue; ImGuiID column_id = TableGetColumnResizeID(table, column_n, table->InstanceCurrent); ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, border_y2_hit); @@ -1163,7 +1170,7 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) KeepAliveID(column_id); bool hovered = false, held = false; - bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick); + bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus); if (pressed && IsMouseDoubleClicked(0)) { TableSetColumnWidthAutoSingle(table, column_n); @@ -1203,6 +1210,7 @@ void ImGui::EndTable() const ImGuiTableFlags flags = table->Flags; ImGuiWindow* inner_window = table->InnerWindow; ImGuiWindow* outer_window = table->OuterWindow; + ImGuiTableTempData* temp_data = table->TempData; IM_ASSERT(inner_window == g.CurrentWindow); IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow); @@ -1215,9 +1223,9 @@ void ImGui::EndTable() TableOpenContextMenu((int)table->HoveredColumnBody); // Finalize table height - inner_window->DC.PrevLineSize = table->HostBackupPrevLineSize; - inner_window->DC.CurrLineSize = table->HostBackupCurrLineSize; - inner_window->DC.CursorMaxPos = table->HostBackupCursorMaxPos; + inner_window->DC.PrevLineSize = temp_data->HostBackupPrevLineSize; + inner_window->DC.CurrLineSize = temp_data->HostBackupCurrLineSize; + inner_window->DC.CursorMaxPos = temp_data->HostBackupCursorMaxPos; const float inner_content_max_y = table->RowPosY2; IM_ASSERT(table->RowPosY2 == inner_window->DC.CursorPos.y); if (inner_window != outer_window) @@ -1264,10 +1272,11 @@ void ImGui::EndTable() #endif // Flatten channels and merge draw calls - table->DrawSplitter.SetCurrentChannel(inner_window->DrawList, 0); + ImDrawListSplitter* splitter = table->DrawSplitter; + splitter->SetCurrentChannel(inner_window->DrawList, 0); if ((table->Flags & ImGuiTableFlags_NoClip) == 0) TableMergeDrawChannels(table); - table->DrawSplitter.Merge(inner_window->DrawList); + splitter->Merge(inner_window->DrawList); // Update ColumnsAutoFitWidth to get us ahead for host using our size to auto-resize without waiting for next BeginTable() const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1); @@ -1309,18 +1318,18 @@ void ImGui::EndTable() // Pop from id stack IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table->ID + table->InstanceCurrent, "Mismatching PushID/PopID!"); - IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= table->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); + IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); PopID(); // Restore window data that we modified const ImVec2 backup_outer_max_pos = outer_window->DC.CursorMaxPos; - inner_window->WorkRect = table->HostBackupWorkRect; - inner_window->ParentWorkRect = table->HostBackupParentWorkRect; + inner_window->WorkRect = temp_data->HostBackupWorkRect; + inner_window->ParentWorkRect = temp_data->HostBackupParentWorkRect; inner_window->SkipItems = table->HostSkipItems; outer_window->DC.CursorPos = table->OuterRect.Min; - outer_window->DC.ItemWidth = table->HostBackupItemWidth; - outer_window->DC.ItemWidthStack.Size = table->HostBackupItemWidthStackSize; - outer_window->DC.ColumnsOffset = table->HostBackupColumnsOffset; + outer_window->DC.ItemWidth = temp_data->HostBackupItemWidth; + outer_window->DC.ItemWidthStack.Size = temp_data->HostBackupItemWidthStackSize; + outer_window->DC.ColumnsOffset = temp_data->HostBackupColumnsOffset; // Layout in outer window // (FIXME: To allow auto-fit and allow desirable effect of SameLine() we dissociate 'used' vs 'ideal' size by overriding @@ -1343,20 +1352,20 @@ void ImGui::EndTable() IM_ASSERT((table->Flags & ImGuiTableFlags_ScrollX) == 0); outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth); } - else if (table->UserOuterSize.x <= 0.0f) + else if (temp_data->UserOuterSize.x <= 0.0f) { const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f; - outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - table->UserOuterSize.x); + outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x); outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth)); } else { outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Max.x); } - if (table->UserOuterSize.y <= 0.0f) + if (temp_data->UserOuterSize.y <= 0.0f) { const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.y : 0.0f; - outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - table->UserOuterSize.y); + outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y); outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y)); } else @@ -1372,8 +1381,14 @@ void ImGui::EndTable() // Clear or restore current table, if any IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table); - g.CurrentTableStack.pop_back(); - g.CurrentTable = g.CurrentTableStack.Size ? g.Tables.GetByIndex(g.CurrentTableStack.back().Index) : NULL; + IM_ASSERT(g.TablesTempDataStacked > 0); + temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL; + g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL; + if (g.CurrentTable) + { + g.CurrentTable->TempData = temp_data; + g.CurrentTable->DrawSplitter = &temp_data->DrawSplitter; + } outer_window->DC.CurrentTableIdx = g.CurrentTable ? g.Tables.GetIndex(g.CurrentTable) : -1; } @@ -1429,7 +1444,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo // Init default visibility/sort state if ((flags & ImGuiTableColumnFlags_DefaultHide) && (table->SettingsLoadedFlags & ImGuiTableFlags_Hideable) == 0) - column->IsEnabled = column->IsEnabledNextFrame = false; + column->IsUserEnabled = column->IsUserEnabledNextFrame = false; if (flags & ImGuiTableColumnFlags_DefaultSort && (table->SettingsLoadedFlags & ImGuiTableFlags_Sortable) == 0) { column->SortOrder = 0; // Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs. @@ -1456,11 +1471,23 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows) IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS); IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit - table->FreezeColumnsRequest = (table->Flags & ImGuiTableFlags_ScrollX) ? (ImGuiTableColumnIdx)columns : 0; + table->FreezeColumnsRequest = (table->Flags & ImGuiTableFlags_ScrollX) ? (ImGuiTableColumnIdx)ImMin(columns, table->ColumnsCount) : 0; table->FreezeColumnsCount = (table->InnerWindow->Scroll.x != 0.0f) ? table->FreezeColumnsRequest : 0; table->FreezeRowsRequest = (table->Flags & ImGuiTableFlags_ScrollY) ? (ImGuiTableColumnIdx)rows : 0; table->FreezeRowsCount = (table->InnerWindow->Scroll.y != 0.0f) ? table->FreezeRowsRequest : 0; table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b + + // Ensure frozen columns are ordered in their section. We still allow multiple frozen columns to be reordered. + // FIXME-TABLE: This work for preserving 2143 into 21|43. How about 4321 turning into 21|43? (preserve relative order in each section) + for (int column_n = 0; column_n < table->FreezeColumnsRequest; column_n++) + { + int order_n = table->DisplayOrderToIndex[column_n]; + if (order_n != column_n && order_n >= table->FreezeColumnsRequest) + { + ImSwap(table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder, table->Columns[table->DisplayOrderToIndex[column_n]].DisplayOrder); + ImSwap(table->DisplayOrderToIndex[order_n], table->DisplayOrderToIndex[column_n]); + } + } } //----------------------------------------------------------------------------- @@ -1469,7 +1496,7 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows) // - TableGetColumnCount() // - TableGetColumnName() // - TableGetColumnName() [Internal] -// - TableSetColumnEnabled() [Internal] +// - TableSetColumnEnabled() // - TableGetColumnFlags() // - TableGetCellBgRect() [Internal] // - TableGetColumnResizeID() [Internal] @@ -1505,10 +1532,12 @@ const char* ImGui::TableGetColumnName(const ImGuiTable* table, int column_n) return &table->ColumnsNames.Buf[column->NameOffset]; } -// Request enabling/disabling a column (often perceived as "showing/hiding" from users point of view) +// Change user accessible enabled/disabled state of a column (often perceived as "showing/hiding" from users point of view) // Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody) -// Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable() -// For the getter you can use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsEnabled) +// - Require table to have the ImGuiTableFlags_Hideable flag because we are manipulating user accessible state. +// - Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable(). +// - For the getter you can test (TableGetColumnFlags() & ImGuiTableColumnFlags_IsEnabled) != 0. +// - Alternative: the ImGuiTableColumnFlags_Disabled is an overriding/master disable flag which will also hide the column from context menu. void ImGui::TableSetColumnEnabled(int column_n, bool enabled) { ImGuiContext& g = *GImGui; @@ -1516,11 +1545,12 @@ void ImGui::TableSetColumnEnabled(int column_n, bool enabled) IM_ASSERT(table != NULL); if (!table) return; + IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above if (column_n < 0) column_n = table->CurrentColumn; IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); ImGuiTableColumn* column = &table->Columns[column_n]; - column->IsEnabledNextFrame = enabled; + column->IsUserEnabledNextFrame = enabled; } // We allow querying for an extra column in order to poll the IsHovered state of the right-most section @@ -1746,7 +1776,7 @@ void ImGui::TableEndRow(ImGuiTable* table) // always followed by a change of clipping rectangle we perform the smallest overwrite possible here. if ((table->Flags & ImGuiTableFlags_NoClip) == 0) window->DrawList->_CmdHeader.ClipRect = table->Bg0ClipRectForDrawCmd.ToVec4(); - table->DrawSplitter.SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0); + table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0); } // Draw row background @@ -1818,7 +1848,7 @@ void ImGui::TableEndRow(ImGuiTable* table) // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect); - table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent); } if (!(table->RowFlags & ImGuiTableRowFlags_Headers)) @@ -1926,21 +1956,22 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n) window->SkipItems = column->IsSkipItems; if (column->IsSkipItems) { - window->DC.LastItemId = 0; - window->DC.LastItemStatusFlags = 0; + ImGuiContext& g = *GImGui; + g.LastItemData.ID = 0; + g.LastItemData.StatusFlags = 0; } if (table->Flags & ImGuiTableFlags_NoClip) { // FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed. - table->DrawSplitter.SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); + table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); //IM_ASSERT(table->DrawSplitter._Current == TABLE_DRAW_CHANNEL_NOCLIP); } else { // FIXME-TABLE: Could avoid this if draw channel is dummy channel? SetWindowClipRectBeforeSetChannel(window, column->ClipRect); - table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); } // Logging @@ -1993,6 +2024,7 @@ float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n) if (table->Flags & ImGuiTableFlags_ScrollX) { // Frozen columns can't reach beyond visible width else scrolling will naturally break. + // (we use DisplayOrder as within a set of multiple frozen column reordering is possible) if (column->DisplayOrder < table->FreezeColumnsRequest) { max_width = (table->InnerClipRect.Max.x - (table->FreezeColumnsRequest - column->DisplayOrder) * min_column_distance) - column->MinX; @@ -2188,7 +2220,7 @@ void ImGui::TablePushBackgroundChannel() // Optimization: avoid SetCurrentChannel() + PushClipRect() table->HostBackupInnerClipRect = window->ClipRect; SetWindowClipRectBeforeSetChannel(window, table->Bg2ClipRectForDrawCmd); - table->DrawSplitter.SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent); } void ImGui::TablePopBackgroundChannel() @@ -2200,7 +2232,7 @@ void ImGui::TablePopBackgroundChannel() // Optimization: avoid PopClipRect() + SetCurrentChannel() SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect); - table->DrawSplitter.SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); + table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent); } // Allocate draw channels. Called by TableUpdateLayout() @@ -2226,7 +2258,7 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table) const int channels_for_bg = 1 + 1 * freeze_row_multiplier; const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || table->VisibleMaskByIndex != table->EnabledMaskByIndex) ? +1 : 0; const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy; - table->DrawSplitter.Split(table->InnerWindow->DrawList, channels_total); + table->DrawSplitter->Split(table->InnerWindow->DrawList, channels_total); table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1); table->Bg2DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG2_FROZEN; table->Bg2DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG2_FROZEN); @@ -2290,7 +2322,7 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table) void ImGui::TableMergeDrawChannels(ImGuiTable* table) { ImGuiContext& g = *GImGui; - ImDrawListSplitter* splitter = &table->DrawSplitter; + ImDrawListSplitter* splitter = table->DrawSplitter; const bool has_freeze_v = (table->FreezeRowsCount > 0); const bool has_freeze_h = (table->FreezeColumnsCount > 0); IM_ASSERT(splitter->_Current == 0); @@ -2301,10 +2333,11 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) ImRect ClipRect; int ChannelsCount; ImBitArray ChannelsMask; + + MergeGroup() { ChannelsCount = 0; } }; int merge_group_mask = 0x00; MergeGroup merge_groups[4]; - memset(merge_groups, 0, sizeof(merge_groups)); // 1. Scan channels and take note of those which can be merged for (int column_n = 0; column_n < table->ColumnsCount; column_n++) @@ -2382,7 +2415,6 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data; ImBitArray remaining_mask; // We need 132-bit of storage - remaining_mask.ClearAllBits(); remaining_mask.SetBitRange(LEADING_DRAW_CHANNELS, splitter->_Count); remaining_mask.ClearBit(table->Bg2DrawChannelUnfrozen); IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN); @@ -2461,7 +2493,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) return; ImDrawList* inner_drawlist = inner_window->DrawList; - table->DrawSplitter.SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0); + table->DrawSplitter->SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0); inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false); // Draw inner border and resizing feedback @@ -2481,7 +2513,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) const bool is_hovered = (table->HoveredColumnBorder == column_n); const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent); const bool is_resizable = (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) == 0; - const bool is_frozen_separator = (table->FreezeColumnsCount != -1 && table->FreezeColumnsCount == order_n + 1); + const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1); if (column->MaxX > table->InnerClipRect.Max.x && !is_resized) continue; @@ -2577,8 +2609,7 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs() if (!table->IsLayoutLocked) TableUpdateLayout(table); - if (table->IsSortSpecsDirty) - TableSortSpecsBuild(table); + TableSortSpecsBuild(table); return &table->SortSpecs; } @@ -2717,13 +2748,18 @@ void ImGui::TableSortSpecsSanitize(ImGuiTable* table) void ImGui::TableSortSpecsBuild(ImGuiTable* table) { - IM_ASSERT(table->IsSortSpecsDirty); - TableSortSpecsSanitize(table); + bool dirty = table->IsSortSpecsDirty; + if (dirty) + { + TableSortSpecsSanitize(table); + table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount); + table->SortSpecs.SpecsDirty = true; // Mark as dirty for user + table->IsSortSpecsDirty = false; // Mark as not dirty for us + } // Write output - table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount); ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data; - if (sort_specs != NULL) + if (dirty && sort_specs != NULL) for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImGuiTableColumn* column = &table->Columns[column_n]; @@ -2736,10 +2772,9 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table) sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; sort_spec->SortDirection = column->SortDirection; } + table->SortSpecs.Specs = sort_specs; table->SortSpecs.SpecsCount = table->SortSpecsCount; - table->SortSpecs.SpecsDirty = true; // Mark as dirty for user - table->IsSortSpecsDirty = false; // Mark as not dirty for us } //------------------------------------------------------------------------- @@ -2759,8 +2794,11 @@ float ImGui::TableGetHeaderRowHeight() float row_height = GetTextLineHeight(); int columns_count = TableGetColumnCount(); for (int column_n = 0; column_n < columns_count; column_n++) - if (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_IsEnabled) + { + ImGuiTableColumnFlags flags = TableGetColumnFlags(column_n); + if ((flags & ImGuiTableColumnFlags_IsEnabled) && !(flags & ImGuiTableColumnFlags_NoHeaderLabel)) row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n)).y); + } row_height += GetStyle().CellPadding.y * 2.0f; return row_height; } @@ -2797,7 +2835,7 @@ void ImGui::TableHeadersRow() // Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them) // - in your own code you may omit the PushID/PopID all-together, provided you know they won't collide // - table->InstanceCurrent is only >0 when we use multiple BeginTable/EndTable calls with same identifier. - const char* name = TableGetColumnName(column_n); + const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n); PushID(table->InstanceCurrent * table->ColumnsCount + column_n); TableHeader(name); PopID(); @@ -2879,7 +2917,6 @@ void ImGui::TableHeader(const char* label) const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); //RenderFrame(bb.Min, bb.Max, col, false, 0.0f); TableSetBgColor(ImGuiTableBgTarget_CellBg, col, table->CurrentColumn); - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); } else { @@ -2887,6 +2924,7 @@ void ImGui::TableHeader(const char* label) if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0) TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn); } + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); if (held) table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n; window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f; @@ -3052,16 +3090,19 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table) for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++) { ImGuiTableColumn* other_column = &table->Columns[other_column_n]; + if (other_column->Flags & ImGuiTableColumnFlags_Disabled) + continue; + const char* name = TableGetColumnName(table, other_column_n); if (name == NULL || name[0] == 0) name = ""; // Make sure we can't hide the last active column bool menu_item_active = (other_column->Flags & ImGuiTableColumnFlags_NoHide) ? false : true; - if (other_column->IsEnabled && table->ColumnsEnabledCount <= 1) + if (other_column->IsUserEnabled && table->ColumnsEnabledCount <= 1) menu_item_active = false; - if (MenuItem(name, NULL, other_column->IsEnabled, menu_item_active)) - other_column->IsEnabledNextFrame = !other_column->IsEnabled; + if (MenuItem(name, NULL, other_column->IsUserEnabled, menu_item_active)) + other_column->IsUserEnabledNextFrame = !other_column->IsUserEnabled; } PopItemFlag(); } @@ -3186,7 +3227,7 @@ void ImGui::TableSaveSettings(ImGuiTable* table) column_settings->DisplayOrder = column->DisplayOrder; column_settings->SortOrder = column->SortOrder; column_settings->SortDirection = column->SortDirection; - column_settings->IsEnabled = column->IsEnabled; + column_settings->IsEnabled = column->IsUserEnabled; column_settings->IsStretch = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? 1 : 0; if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) == 0) save_ref_scale = true; @@ -3200,7 +3241,7 @@ void ImGui::TableSaveSettings(ImGuiTable* table) settings->SaveFlags |= ImGuiTableFlags_Reorderable; if (column->SortOrder != -1) settings->SaveFlags |= ImGuiTableFlags_Sortable; - if (column->IsEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0)) + if (column->IsUserEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0)) settings->SaveFlags |= ImGuiTableFlags_Hideable; } settings->SaveFlags &= table->Flags; @@ -3258,7 +3299,7 @@ void ImGui::TableLoadSettings(ImGuiTable* table) else column->DisplayOrder = (ImGuiTableColumnIdx)column_n; display_order_mask |= (ImU64)1 << column->DisplayOrder; - column->IsEnabled = column->IsEnabledNextFrame = column_settings->IsEnabled; + column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled; column->SortOrder = column_settings->SortOrder; column->SortDirection = column_settings->SortDirection; } @@ -3277,8 +3318,9 @@ void ImGui::TableLoadSettings(ImGuiTable* table) static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiContext& g = *ctx; - for (int i = 0; i != g.Tables.GetSize(); i++) - g.Tables.GetByIndex(i)->SettingsOffset = -1; + for (int i = 0; i != g.Tables.GetMapSize(); i++) + if (ImGuiTable* table = g.Tables.TryGetMapData(i)) + table->SettingsOffset = -1; g.SettingsTables.clear(); } @@ -3286,12 +3328,12 @@ static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandle static void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiContext& g = *ctx; - for (int i = 0; i != g.Tables.GetSize(); i++) - { - ImGuiTable* table = g.Tables.GetByIndex(i); - table->IsSettingsRequestLoad = true; - table->SettingsOffset = -1; - } + for (int i = 0; i != g.Tables.GetMapSize(); i++) + if (ImGuiTable* table = g.Tables.TryGetMapData(i)) + { + table->IsSettingsRequestLoad = true; + table->SettingsOffset = -1; + } } static void* TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) @@ -3420,10 +3462,9 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) //IMGUI_DEBUG_LOG("TableGcCompactTransientBuffers() id=0x%08X\n", table->ID); ImGuiContext& g = *GImGui; IM_ASSERT(table->MemoryCompacted == false); - table->DrawSplitter.ClearFreeMemory(); - table->SortSpecsMulti.clear(); table->SortSpecs.Specs = NULL; - table->IsSortSpecsDirty = true; + table->SortSpecsMulti.clear(); + table->IsSortSpecsDirty = true; // FIXME: shouldn't have to leak into user performing a sort table->ColumnsNames.clear(); table->MemoryCompacted = true; for (int n = 0; n < table->ColumnsCount; n++) @@ -3431,6 +3472,12 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) g.TablesLastTimeActive[g.Tables.GetIndex(table)] = -1.0f; } +void ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data) +{ + temp_data->DrawSplitter.ClearFreeMemory(); + temp_data->LastTimeActive = -1.0f; +} + // Compact and remove unused settings data (currently only used by TestEngine) void ImGui::TableGcCompactSettings() { @@ -3940,7 +3987,7 @@ void ImGui::EndColumns() const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH; const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2)); KeepAliveID(column_id); - if (IsClippedEx(column_hit_rect, column_id, false)) + if (IsClippedEx(column_hit_rect, column_id)) // FIXME: Can be removed or replaced with a lower-level test continue; bool hovered = false, held = false; diff --git a/r5dev/thirdparty/imgui/src/imgui_utility.cpp b/r5dev/thirdparty/imgui/src/imgui_utility.cpp new file mode 100644 index 00000000..fda55ca1 --- /dev/null +++ b/r5dev/thirdparty/imgui/src/imgui_utility.cpp @@ -0,0 +1,103 @@ +/*----------------------------------------------------------------------------- + * _imgui_utility.cpp + *-----------------------------------------------------------------------------*/ + +#include "core/stdafx.h" +#include "engine/sys_utils.h" +#include "thirdparty/imgui/include/imgui_utility.h" + +int Stricmp(const char* s1, const char* s2) +{ + int d; + while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) + { + s1++; s2++; + } + return d; +} + +int Strnicmp(const char* s1, const char* s2, int n) +{ + int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) + { + s1++; s2++; n--; + } + return d; +} + +char* Strdup(const char* s) +{ + IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = malloc(len); IM_ASSERT(buf); if (buf != NULL) + { + return (char*)memcpy(buf, (const void*)s, len); + } + return NULL; +} + +void Strtrim(char* s) +{ + char* str_end = s + strlen(s); + + while (str_end > s && str_end[-1] == ' ') + str_end--; *str_end = 0; +} + +void ImGuiConfig::Load() +{ + std::filesystem::path fsPath = std::filesystem::current_path() /= "platform\\imgui.json"; // Get current path + imgui.json + DevMsg(eDLL_T::MS, "Loading ImGui config file '%s'\n", fsPath.string().c_str()); + + nlohmann::json jsIn; + try + { + std::ifstream configFile(fsPath, std::ios::binary); // Parse config file. + configFile >> jsIn; + configFile.close(); + + if (!jsIn.is_null()) + { + if (!jsIn["config"].is_null()) + { + // IConsole + IConsole_Config.m_nBind0 = jsIn["config"]["IConsole"]["bind0"].get(); + IConsole_Config.m_nBind1 = jsIn["config"]["IConsole"]["bind1"].get(); + IConsole_Config.m_nAutoClearLimit = jsIn["config"]["IConsole"]["autoClearLimit"].get(); + IConsole_Config.m_bAutoClear = jsIn["config"]["IConsole"]["autoClear"].get(); + + // IBrowser + IBrowser_Config.m_nBind0 = jsIn["config"]["IBrowser"]["bind0"].get(); + IBrowser_Config.m_nBind1 = jsIn["config"]["IBrowser"]["bind1"].get(); + } + } + } + catch (const std::exception& ex) + { + DevMsg(eDLL_T::MS, "ImGui config file '%s' not found. Changing the settings in the console or server browser options re-create's it. Exception: '%s'\n", fsPath.string().c_str(), ex.what()); + return; + } +} + +void ImGuiConfig::Save() +{ + nlohmann::json jsOut; + + // IConsole + jsOut["config"]["IConsole"]["bind0"] = IConsole_Config.m_nBind0; + jsOut["config"]["IConsole"]["bind1"] = IConsole_Config.m_nBind1; + jsOut["config"]["IConsole"]["autoClearLimit"] = IConsole_Config.m_nAutoClearLimit; + jsOut["config"]["IConsole"]["autoClear"] = IConsole_Config.m_bAutoClear; + + // IBrowser + jsOut["config"]["IBrowser"]["bind0"] = IBrowser_Config.m_nBind0; + jsOut["config"]["IBrowser"]["bind1"] = IBrowser_Config.m_nBind1; + + std::filesystem::path fsPath = std::filesystem::current_path() /= "platform\\imgui.json"; // Get current path + imgui.json + + DevMsg(eDLL_T::MS, "Saving ImGui config file '%s'\n", fsPath.string().c_str()); + std::ofstream outFile(fsPath, std::ios::out | std::ios::trunc); // Write config file. + + outFile << jsOut.dump(4); // Dump it into config file. + outFile.close(); // Close the file handle. +} + +ImGuiConfig* g_pImGuiConfig = new ImGuiConfig(); diff --git a/external/imgui/src/imgui_widgets.cpp b/r5dev/thirdparty/imgui/src/imgui_widgets.cpp similarity index 89% rename from external/imgui/src/imgui_widgets.cpp rename to r5dev/thirdparty/imgui/src/imgui_widgets.cpp index 2f6b05a2..8be4538b 100644 --- a/external/imgui/src/imgui_widgets.cpp +++ b/r5dev/thirdparty/imgui/src/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.83 WIP +// dear imgui, v1.86 // (widgets code) /* @@ -32,13 +32,13 @@ Index of this file: #define _CRT_SECURE_NO_WARNINGS #endif -#include "imgui.h" +#include "thirdparty/imgui/include/imgui.h" #ifndef IMGUI_DISABLE #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif -#include "imgui_internal.h" +#include "thirdparty/imgui/include/imgui_internal.h" // System includes #include // toupper @@ -59,6 +59,8 @@ Index of this file: #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif // Clang/GCC warnings with -Weverything @@ -150,9 +152,13 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; - ImGuiContext& g = *GImGui; - IM_ASSERT(text != NULL); + + // Accept null ranges + if (text == text_end) + text = text_end = ""; + + // Calculate length const char* text_begin = text; if (text_end == NULL) text_end = text + strlen(text); // FIXME-OPT @@ -199,7 +205,7 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); while (line < text_end) { - if (IsClippedEx(line_rect, 0, false)) + if (IsClippedEx(line_rect, 0)) break; const char* line_end = (const char*)memchr(line, '\n', text_end - line); @@ -267,6 +273,7 @@ void ImGui::TextV(const char* fmt, va_list args) if (window->SkipItems) return; + // FIXME-OPT: Handle the %s shortcut? ImGuiContext& g = *GImGui; const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); TextEx(g.TempBuffer, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); @@ -486,14 +493,6 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); - if (flags & ImGuiButtonFlags_Disabled) - { - if (out_hovered) *out_hovered = false; - if (out_held) *out_held = false; - if (g.ActiveId == id) ClearActiveID(); - return false; - } - // Default only reacts to left mouse button if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0) flags |= ImGuiButtonFlags_MouseButtonDefault_; @@ -508,7 +507,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool g.HoveredWindow = window; #ifdef IMGUI_ENABLE_TEST_ENGINE - if (id != 0 && window->DC.LastItemId != id) + if (id != 0 && g.LastItemData.ID != id) IMGUI_TEST_ENGINE_ITEM_ADD(bb, id); #endif @@ -525,7 +524,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool { hovered = true; SetHoveredID(id); - if (CalcTypematicRepeatAmount(g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, g.HoveredIdTimer + 0.0001f, DRAGDROP_HOLD_TO_OPEN_TIMER, 0.00f)) + if (g.HoveredIdTimer - g.IO.DeltaTime <= DRAGDROP_HOLD_TO_OPEN_TIMER && g.HoveredIdTimer >= DRAGDROP_HOLD_TO_OPEN_TIMER) { pressed = true; g.DragDropHoldJustPressedId = id; @@ -565,13 +564,15 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool SetFocusID(id, window); FocusWindow(window); } - if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[mouse_button_clicked])) + if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseClickedCount[mouse_button_clicked] == 2)) { pressed = true; if (flags & ImGuiButtonFlags_NoHoldingActiveId) ClearActiveID(); else SetActiveID(id, window); // Hold on ID + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + SetFocusID(id, window); g.ActiveIdMouseButton = mouse_button_clicked; FocusWindow(window); } @@ -582,6 +583,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool const bool has_repeated_at_least_once = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; if (!has_repeated_at_least_once) pressed = true; + if (!(flags & ImGuiButtonFlags_NoNavFocus)) + SetFocusID(id, window); ClearActiveID(); } @@ -606,13 +609,12 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool bool nav_activated_by_code = (g.NavActivateId == id); bool nav_activated_by_inputs = IsNavInputTest(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed); if (nav_activated_by_code || nav_activated_by_inputs) - pressed = true; - if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id) { // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. - g.NavActivateId = id; // This is so SetActiveId assign a Nav source + pressed = true; SetActiveID(id, window); - if ((nav_activated_by_code || nav_activated_by_inputs) && !(flags & ImGuiButtonFlags_NoNavFocus)) + g.ActiveIdSource = ImGuiInputSource_Nav; + if (!(flags & ImGuiButtonFlags_NoNavFocus)) SetFocusID(id, window); } } @@ -639,7 +641,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if ((release_in || release_anywhere) && !g.DragDropActive) { // Report as pressed when releasing the mouse (this is the most common path) - bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDownWasDoubleClick[mouse_button]; + bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2; bool is_repeating_already = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps if (!is_double_click_release && !is_repeating_already) pressed = true; @@ -686,8 +688,9 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags if (!ItemAdd(bb, id)) return false; - if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) + if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat) flags |= ImGuiButtonFlags_Repeat; + bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); @@ -704,7 +707,7 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) // CloseCurrentPopup(); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } @@ -762,7 +765,7 @@ bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiBu if (!ItemAdd(bb, id)) return false; - if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) + if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat) flags |= ImGuiButtonFlags_Repeat; bool hovered, held; @@ -893,7 +896,9 @@ void ImGui::Scrollbar(ImGuiAxis axis) } float size_avail = window->InnerRect.Max[axis] - window->InnerRect.Min[axis]; float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f; - ScrollbarEx(bb, id, axis, &window->Scroll[axis], size_avail, size_contents, rounding_corners); + ImS64 scroll = (ImS64)window->Scroll[axis]; + ScrollbarEx(bb, id, axis, &scroll, (ImS64)size_avail, (ImS64)size_contents, rounding_corners); + window->Scroll[axis] = (float)scroll; } // Vertical/Horizontal scrollbar @@ -902,7 +907,7 @@ void ImGui::Scrollbar(ImGuiAxis axis) // - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar // - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. // Still, the code should probably be made simpler.. -bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, float* p_scroll_v, float size_avail_v, float size_contents_v, ImDrawFlags flags) +bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_avail_v, ImS64 size_contents_v, ImDrawFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -933,8 +938,8 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) // But we maintain a minimum size in pixel to allow for the user to still aim inside. IM_ASSERT(ImMax(size_contents_v, size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. - const float win_size_v = ImMax(ImMax(size_contents_v, size_avail_v), 1.0f); - const float grab_h_pixels = ImClamp(scrollbar_size_v * (size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v); + const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_avail_v), (ImS64)1); + const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_avail_v / (float)win_size_v), style.GrabMinSize, scrollbar_size_v); const float grab_h_norm = grab_h_pixels / scrollbar_size_v; // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). @@ -942,13 +947,13 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa bool hovered = false; ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); - float scroll_max = ImMax(1.0f, size_contents_v - size_avail_v); - float scroll_ratio = ImSaturate(*p_scroll_v / scroll_max); + const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_avail_v); + float scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space if (held && allow_interaction && grab_h_norm < 1.0f) { - float scrollbar_pos_v = bb.Min[axis]; - float mouse_pos_v = g.IO.MousePos[axis]; + const float scrollbar_pos_v = bb.Min[axis]; + const float mouse_pos_v = g.IO.MousePos[axis]; // Click position in scrollbar normalized space (0.0f->1.0f) const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); @@ -968,10 +973,10 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, floa // Apply scroll (p_scroll_v will generally point on one member of window->Scroll) // It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm)); - *p_scroll_v = IM_ROUND(scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v)); + *p_scroll_v = (ImS64)(scroll_v_norm * scroll_max); // Update values for rendering - scroll_ratio = ImSaturate(*p_scroll_v / scroll_max); + scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max); grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Update distance to grab now that we have seeked and saturated @@ -1081,7 +1086,7 @@ bool ImGui::Checkbox(const char* label, bool* v) ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, id)) { - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return false; } @@ -1097,7 +1102,7 @@ bool ImGui::Checkbox(const char* label, bool* v) RenderNavHighlight(total_bb, id); RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); - bool mixed_value = (window->DC.ItemFlags & ImGuiItemFlags_MixedValue) != 0; + bool mixed_value = (g.LastItemData.InFlags & ImGuiItemFlags_MixedValue) != 0; if (mixed_value) { // Undocumented tristate/mixed/indeterminate checkbox (#2644) @@ -1117,7 +1122,7 @@ bool ImGui::Checkbox(const char* label, bool* v) if (label_size.x > 0.0f) RenderText(label_pos, label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); return pressed; } @@ -1129,11 +1134,11 @@ bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value) bool pressed; if (!all_on && any_on) { - ImGuiWindow* window = GetCurrentWindow(); - ImGuiItemFlags backup_item_flags = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_MixedValue; + ImGuiContext& g = *GImGui; + ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_MixedValue; pressed = Checkbox(label, &all_on); - window->DC.ItemFlags = backup_item_flags; + g.CurrentItemFlags = backup_item_flags; } else { @@ -1219,7 +1224,7 @@ bool ImGui::RadioButton(const char* label, bool active) if (label_size.x > 0.0f) RenderText(label_pos, label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return pressed; } @@ -1388,11 +1393,20 @@ void ImGui::SeparatorEx(ImGuiSeparatorFlags flags) if (g.GroupStack.Size > 0 && g.GroupStack.back().WindowID == window->ID) x1 += window->DC.Indent.x; + // FIXME-WORKRECT: In theory we should simply be using WorkRect.Min.x/Max.x everywhere but it isn't aesthetically what we want, + // need to introduce a variant of WorkRect for that purpose. (#4787) + if (ImGuiTable* table = g.CurrentTable) + { + x1 = table->Columns[table->CurrentColumn].MinX; + x2 = table->Columns[table->CurrentColumn].MaxX; + } + ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL; if (columns) PushColumnsBackground(); // We don't provide our width to the layout so that it doesn't get feed back into AutoFit + // FIXME: This prevents ->CursorMaxPos based bounding box evaluation from working (e.g. TableEndCell) const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness_draw)); ItemSize(ImVec2(0.0f, thickness_layout)); const bool item_visible = ItemAdd(bb, 0); @@ -1421,7 +1435,7 @@ void ImGui::Separator() // Those flags should eventually be overridable by the user ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; - flags |= ImGuiSeparatorFlags_SpanAllColumns; + flags |= ImGuiSeparatorFlags_SpanAllColumns; // NB: this only applies to legacy Columns() api as they relied on Separator() a lot. SeparatorEx(flags); } @@ -1431,10 +1445,10 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; + const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; bool item_add = ItemAdd(bb, id); - window->DC.ItemFlags = item_flags_backup; + g.CurrentItemFlags = item_flags_backup; if (!item_add) return false; @@ -1442,10 +1456,12 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float ImRect bb_interact = bb; bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap); + if (hovered) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb if (g.ActiveId != id) SetItemAllowOverlap(); - if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) + if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); ImRect bb_render = bb; @@ -1535,8 +1551,12 @@ void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_exc //------------------------------------------------------------------------- // [SECTION] Widgets: ComboBox //------------------------------------------------------------------------- +// - CalcMaxPopupHeightFromItemCount() [Internal] // - BeginCombo() +// - BeginComboPopup() [Internal] // - EndCombo() +// - BeginComboPreview() [Internal] +// - EndComboPreview() [Internal] // - Combo() //------------------------------------------------------------------------- @@ -1550,79 +1570,99 @@ static float CalcMaxPopupHeightFromItemCount(int items_count) bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags) { - // Always consume the SetNextWindowSizeConstraint() call in our early return paths ImGuiContext& g = *GImGui; - bool has_window_size_constraint = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) != 0; - g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; - ImGuiWindow* window = GetCurrentWindow(); + + ImGuiNextWindowDataFlags backup_next_window_data_flags = g.NextWindowData.Flags; + g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values if (window->SkipItems) return false; - IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together - const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); + IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); const ImVec2 label_size = CalcTextSize(label, NULL, true); - const float expected_w = CalcItemWidth(); - const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : expected_w; - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth(); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); + const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) + if (!ItemAdd(total_bb, id, &bb)) return false; + // Open on click bool hovered, held; - bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); - bool popup_open = IsPopupOpen(id, ImGuiPopupFlags_None); + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + const ImGuiID popup_id = ImHashStr("##ComboPopup", 0, id); + bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None); + if (pressed && !popup_open) + { + OpenPopupEx(popup_id, ImGuiPopupFlags_None); + popup_open = true; + } + // Render shape const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - const float value_x2 = ImMax(frame_bb.Min.x, frame_bb.Max.x - arrow_size); - RenderNavHighlight(frame_bb, id); + const float value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size); + RenderNavHighlight(bb, id); if (!(flags & ImGuiComboFlags_NoPreview)) - window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(value_x2, frame_bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft); + window->DrawList->AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft); if (!(flags & ImGuiComboFlags_NoArrowButton)) { ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button); ImU32 text_col = GetColorU32(ImGuiCol_Text); - window->DrawList->AddRectFilled(ImVec2(value_x2, frame_bb.Min.y), frame_bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight); - if (value_x2 + arrow_size - style.FramePadding.x <= frame_bb.Max.x) - RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); + window->DrawList->AddRectFilled(ImVec2(value_x2, bb.Min.y), bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight); + if (value_x2 + arrow_size - style.FramePadding.x <= bb.Max.x) + RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f); } - RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding); + RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding); + + // Custom preview + if (flags & ImGuiComboFlags_CustomPreview) + { + g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y); + IM_ASSERT(preview_value == NULL || preview_value[0] == 0); + preview_value = NULL; + } + + // Render preview and label if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) { - ImVec2 preview_pos = frame_bb.Min + style.FramePadding; if (g.LogEnabled) LogSetNextTextDecoration("{", "}"); - RenderTextClipped(preview_pos, ImVec2(value_x2, frame_bb.Max.y), preview_value, NULL, NULL, ImVec2(0.0f, 0.0f)); + RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL, NULL); } if (label_size.x > 0) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - if ((pressed || g.NavActivateId == id) && !popup_open) - { - if (window->DC.NavLayerCurrent == 0) - window->NavLastIds[0] = id; - OpenPopupEx(id, ImGuiPopupFlags_None); - popup_open = true; - } + RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label); if (!popup_open) return false; - if (has_window_size_constraint) + g.NextWindowData.Flags = backup_next_window_data_flags; + return BeginComboPopup(popup_id, bb, flags); +} + +bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags) +{ + ImGuiContext& g = *GImGui; + if (!IsPopupOpen(popup_id, ImGuiPopupFlags_None)) + { + g.NextWindowData.ClearFlags(); + return false; + } + + // Set popup size + float w = bb.GetWidth(); + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) { - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint; g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); } else { if ((flags & ImGuiComboFlags_HeightMask_) == 0) flags |= ImGuiComboFlags_HeightRegular; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one int popup_max_height_in_items = -1; if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; @@ -1630,30 +1670,27 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); } + // This is essentially a specialized version of BeginPopupEx() char name[16]; ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth - // Position the window given a custom constraint (peak into expected window size so we can position it) - // This might be easier to express with an hypothetical SetNextWindowPosConstraints() function. + // Set position given a custom constraint (peak into expected window size so we can position it) + // FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function? + // FIXME: This might be moved to Begin() or at least around the same spot where Tooltips and other Popups are calling FindBestWindowPosForPopupEx()? if (ImGuiWindow* popup_window = FindWindowByName(name)) if (popup_window->WasActive) { // Always override 'AutoPosLastDirection' to not leave a chance for a past value to affect us. ImVec2 size_expected = CalcWindowNextAutoFitSize(popup_window); - if (flags & ImGuiComboFlags_PopupAlignLeft) - popup_window->AutoPosLastDirection = ImGuiDir_Left; // "Below, Toward Left" - else - popup_window->AutoPosLastDirection = ImGuiDir_Down; // "Below, Toward Right (default)" - ImRect r_outer = GetWindowAllowedExtentRect(popup_window); - ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox); + popup_window->AutoPosLastDirection = (flags & ImGuiComboFlags_PopupAlignLeft) ? ImGuiDir_Left : ImGuiDir_Down; // Left = "Below, Toward Left", Down = "Below, Toward Right (default)" + ImRect r_outer = GetPopupAllowedExtentRect(popup_window); + ImVec2 pos = FindBestWindowPosForPopupEx(bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, bb, ImGuiPopupPositionPolicy_ComboBox); SetNextWindowPos(pos); } // We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx() ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove; - - // Horizontally align ourselves with the framed text - PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(style.FramePadding.x, style.WindowPadding.y)); + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(g.Style.FramePadding.x, g.Style.WindowPadding.y)); // Horizontally align ourselves with the framed text bool ret = Begin(name, NULL, window_flags); PopStyleVar(); if (!ret) @@ -1670,6 +1707,57 @@ void ImGui::EndCombo() EndPopup(); } +// Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements +// (Experimental, see GitHub issues: #1658, #4168) +bool ImGui::BeginComboPreview() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; + + if (window->SkipItems || !window->ClipRect.Overlaps(g.LastItemData.Rect)) // FIXME: Because we don't have a ImGuiItemStatusFlags_Visible flag to test last ItemAdd() result + return false; + IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag? + if (!window->ClipRect.Contains(preview_data->PreviewRect)) // Narrower test (optional) + return false; + + // FIXME: This could be contained in a PushWorkRect() api + preview_data->BackupCursorPos = window->DC.CursorPos; + preview_data->BackupCursorMaxPos = window->DC.CursorMaxPos; + preview_data->BackupCursorPosPrevLine = window->DC.CursorPosPrevLine; + preview_data->BackupPrevLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; + preview_data->BackupLayout = window->DC.LayoutType; + window->DC.CursorPos = preview_data->PreviewRect.Min + g.Style.FramePadding; + window->DC.CursorMaxPos = window->DC.CursorPos; + window->DC.LayoutType = ImGuiLayoutType_Horizontal; + PushClipRect(preview_data->PreviewRect.Min, preview_data->PreviewRect.Max, true); + + return true; +} + +void ImGui::EndComboPreview() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; + + // FIXME: Using CursorMaxPos approximation instead of correct AABB which we will store in ImDrawCmd in the future + ImDrawList* draw_list = window->DrawList; + if (window->DC.CursorMaxPos.x < preview_data->PreviewRect.Max.x && window->DC.CursorMaxPos.y < preview_data->PreviewRect.Max.y) + if (draw_list->CmdBuffer.Size > 1) // Unlikely case that the PushClipRect() didn't create a command + { + draw_list->_CmdHeader.ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 2].ClipRect; + draw_list->_TryMergeDrawCmds(); + } + PopClipRect(); + window->DC.CursorPos = preview_data->BackupCursorPos; + window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, preview_data->BackupCursorMaxPos); + window->DC.CursorPosPrevLine = preview_data->BackupCursorPosPrevLine; + window->DC.PrevLineTextBaseOffset = preview_data->BackupPrevLineTextBaseOffset; + window->DC.LayoutType = preview_data->BackupLayout; + preview_data->PreviewRect = ImRect(); +} + // Getter for the old Combo() API: const char*[] static bool Items_ArrayGetter(void* data, int idx, const char** out_text) { @@ -1722,7 +1810,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi bool value_changed = false; for (int i = 0; i < items_count; i++) { - PushID((void*)(intptr_t)i); + PushID(i); const bool item_selected = (i == *current_item); const char* item_text; if (!items_getter(data, i, &item_text)) @@ -1738,8 +1826,9 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi } EndCombo(); + if (value_changed) - MarkItemEdited(g.CurrentWindow->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); return value_changed; } @@ -1980,13 +2069,15 @@ bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_b { // All other types assign constant // We don't bother handling support for legacy operators since they are a little too crappy. Instead we will later implement a proper expression evaluator in the future. - sscanf(buf, format, p_data); + if (sscanf(buf, format, p_data) < 1) + return false; } else { // Small types need a 32-bit buffer to receive the result from scanf() int v32; - sscanf(buf, format, &v32); + if (sscanf(buf, format, &v32) < 1) + return false; if (data_type == ImGuiDataType_S8) *(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX); else if (data_type == ImGuiDataType_U8) @@ -2273,7 +2364,7 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v } if (g.ActiveId != id) return false; - if ((g.CurrentWindow->DC.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) + if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) return false; switch (data_type) @@ -2311,8 +2402,9 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) + if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) return false; // Default format string when passing NULL @@ -2323,33 +2415,30 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, // Tabbing or CTRL-clicking on Drag turns it into an InputText const bool hovered = ItemHoverable(frame_bb, id); - const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { - const bool focus_requested = temp_input_allowed && FocusableItemRegister(window, id); + const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); - const bool double_clicked = (hovered && g.IO.MouseDoubleClicked[0]); - if (focus_requested || clicked || double_clicked || g.NavActivateId == id || g.NavInputId == id) + const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2); + if (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavInputId == id)) - { - temp_input_is_active = true; - FocusableItemUnregister(window); - } + if (temp_input_allowed) + if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id) + temp_input_is_active = true; } + // Experimental: simple click (without moving) turns Drag into an InputText - // FIXME: Currently polling ImGuiConfigFlags_IsTouchScreen, may either poll an hypothetical ImGuiBackendFlags_HasKeyboard and/or an explicit drag settings. if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active) if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR)) { - g.NavInputId = id; + g.NavActivateId = g.NavActivateInputId = id; + g.NavActivateFlags = ImGuiActivateFlags_PreferInput; temp_input_is_active = true; - FocusableItemUnregister(window); } } @@ -2361,7 +2450,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, } // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); @@ -2380,7 +2469,7 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return value_changed; } @@ -2468,6 +2557,7 @@ bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_cu TextEx(label, FindRenderedTextEnd(label)); EndGroup(); PopID(); + return value_changed; } @@ -2880,7 +2970,7 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flag! Has the 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); ImGuiContext& g = *GImGui; - if ((g.CurrentWindow->DC.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) + if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) return false; switch (data_type) @@ -2930,8 +3020,9 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) + if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0)) return false; // Default format string when passing NULL @@ -2942,23 +3033,19 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat // Tabbing or CTRL-clicking on Slider turns it into an input box const bool hovered = ItemHoverable(frame_bb, id); - const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0; bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { - const bool focus_requested = temp_input_allowed && FocusableItemRegister(window, id); + const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = (hovered && g.IO.MouseClicked[0]); - if (focus_requested || clicked || g.NavActivateId == id || g.NavInputId == id) + if (input_requested_by_tabbing || clicked || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - if (temp_input_allowed && (focus_requested || (clicked && g.IO.KeyCtrl) || g.NavInputId == id)) - { + if (temp_input_allowed && (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id)) temp_input_is_active = true; - FocusableItemUnregister(window); - } } } @@ -2970,7 +3057,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat } // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); @@ -2994,7 +3081,7 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); return value_changed; } @@ -3109,7 +3196,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d format = PatchFormatStringFloatToInt(format); const bool hovered = ItemHoverable(frame_bb, id); - if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) + if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavActivateInputId == id) { SetActiveID(id, window); SetFocusID(id, window); @@ -3118,7 +3205,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d } // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); RenderNavHighlight(frame_bb, id); RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding); @@ -3285,7 +3372,7 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* ClearActiveID(); g.CurrentWindow->DC.CursorPos = bb.Min; - bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags); + bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem); if (init) { // First frame we started displaying the InputText widget, we expect it to take the active id. @@ -3373,7 +3460,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data style.FramePadding.x = style.FramePadding.y; ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups; if (flags & ImGuiInputTextFlags_ReadOnly) - button_flags |= ImGuiButtonFlags_Disabled; + BeginDisabled(); SameLine(0, style.ItemInnerSpacing.x); if (ButtonEx("-", ImVec2(button_size, button_size), button_flags)) { @@ -3386,6 +3473,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data DataTypeApplyOp(data_type, '+', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step); value_changed = true; } + if (flags & ImGuiInputTextFlags_ReadOnly) + EndDisabled(); const char* label_end = FindRenderedTextEnd(label); if (label != label_end) @@ -3404,7 +3493,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, p_data, format); } if (value_changed) - MarkItemEdited(window->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); return value_changed; } @@ -3582,12 +3671,12 @@ static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* t namespace ImStb { -static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } -static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->TextW[idx]; } -static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *GImGui; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } +static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenW; } +static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { return obj->TextW[idx]; } +static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *GImGui; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x200000 ? 0 : key; } static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; -static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) +static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx) { const ImWchar* text = obj->TextW.Data; const ImWchar* text_remaining = NULL; @@ -3600,19 +3689,21 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* ob r->num_chars = (int)(text_remaining - (text + line_start_idx)); } -static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } -static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } -#ifdef __APPLE__ // FIXME: Move setting to IO structure -static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx]) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } -#else -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } -#endif +// When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators. +static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|' || c=='\n' || c=='\r'; } +static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; } +static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx])) : 1; } +static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } +static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } #define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h -#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL +#ifdef __APPLE__ // FIXME: Move setting to IO structure +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_MAC +#else +static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_WIN +#endif -static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) +static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n) { ImWchar* dst = obj->TextW.Data + pos; @@ -3628,9 +3719,9 @@ static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) *dst = '\0'; } -static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) +static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ImWchar* new_text, int new_text_len) { - const bool is_resizable = (obj->UserFlags & ImGuiInputTextFlags_CallbackResize) != 0; + const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0; const int text_len = obj->CurLenW; IM_ASSERT(pos <= text_len); @@ -3680,11 +3771,11 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im #define STB_TEXTEDIT_K_SHIFT 0x400000 #define STB_TEXTEDIT_IMPLEMENTATION -#include "imstb_textedit.h" +#include "thirdparty/imgui/include/imstb_textedit.h" // stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling // the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?) -static void stb_textedit_replace(STB_TEXTEDIT_STRING* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len) +static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len) { stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len); ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW); @@ -3773,6 +3864,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f unsigned int c = *p_char; // Filter non-printable (NB: isprint is unreliable! see #2467) + bool apply_named_filters = true; if (c < 0x20) { bool pass = false; @@ -3780,6 +3872,7 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); if (!pass) return false; + apply_named_filters = false; // Override named filters below so newline and tabs can still be inserted. } if (input_source != ImGuiInputSource_Clipboard) @@ -3798,13 +3891,14 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f return false; // Generic named filters - if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific)) + if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific))) { - // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf. + // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf to use e.g. ',' instead of '.'. // The standard mandate that programs starts in the "C" locale where the decimal point is '.'. // We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point. // Change the default decimal_point with: // ImGui::GetCurrentContext()->PlatformLocaleDecimalPoint = *localeconv()->decimal_point; + // Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions. ImGuiContext& g = *GImGui; const unsigned c_decimal_point = (unsigned int)g.PlatformLocaleDecimalPoint; @@ -3883,7 +3977,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_resizable) IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! - if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope, + if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar) BeginGroup(); const ImGuiID id = window->GetID(label); const ImVec2 label_size = CalcTextSize(label, NULL, true); @@ -3895,21 +3989,29 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ ImGuiWindow* draw_window = window; ImVec2 inner_size = frame_size; + ImGuiItemStatusFlags item_status_flags = 0; + ImGuiLastItemData item_data_backup; if (is_multiline) { - if (!ItemAdd(total_bb, id, &frame_bb)) + ImVec2 backup_pos = window->DC.CursorPos; + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) { - ItemSize(total_bb, style.FramePadding.y); EndGroup(); return false; } + item_status_flags = g.LastItemData.StatusFlags; + item_data_backup = g.LastItemData; + window->DC.CursorPos = backup_pos; // We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug. + // FIXME-NAV: Pressing NavActivate will trigger general child activation right before triggering our own below. Harmless but bizarre. PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), true, ImGuiWindowFlags_NoMove); - PopStyleVar(2); + PopStyleVar(3); PopStyleColor(); if (!child_visible) { @@ -3918,15 +4020,18 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ return false; } draw_window = g.CurrentWindow; // Child window - draw_window->DC.NavLayerActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it. + draw_window->DC.NavLayersActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it. draw_window->DC.CursorPos += style.FramePadding; inner_size.x -= draw_window->ScrollbarSizes.x; } else { + // Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd) ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) - return false; + if (!(flags & ImGuiInputTextFlags_MergedItem)) + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) + return false; + item_status_flags = g.LastItemData.StatusFlags; } const bool hovered = ItemHoverable(frame_bb, id); if (hovered) @@ -3935,22 +4040,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We are only allowed to access the state if we are already the active widget. ImGuiInputTextState* state = GetInputTextState(id); - const bool focus_requested = FocusableItemRegister(window, id); - const bool focus_requested_by_code = focus_requested && (g.TabFocusRequestCurrWindow == window && g.TabFocusRequestCurrCounterRegular == window->DC.FocusCounterRegular); - const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; + const bool input_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; + const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_Keyboard)); const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); - bool clear_active_id = false; - bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline); + bool select_all = false; float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); - const bool init_make_active = (focus_requested || user_clicked || user_scroll_finish || user_nav_input_start); + const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_tabbing); const bool init_state = (init_make_active || user_scroll_active); if ((init_state && g.ActiveId != id) || init_changed_specs) { @@ -3986,13 +4088,20 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->ID = id; state->ScrollX = 0.0f; stb_textedit_initialize_state(&state->Stb, !is_multiline); - if (!is_multiline && focus_requested_by_code) + } + + if (!is_multiline) + { + if (flags & ImGuiInputTextFlags_AutoSelectAll) + select_all = true; + if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState))) + select_all = true; + if (input_requested_by_tabbing || (user_clicked && io.KeyCtrl)) select_all = true; } + if (flags & ImGuiInputTextFlags_AlwaysOverwrite) state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863) - if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) - select_all = true; } if (g.ActiveId != id && init_make_active) @@ -4025,7 +4134,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Lock the decision of whether we are going to take the path displaying the cursor or selection const bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active); - bool render_selection = state && state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); + bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); bool value_changed = false; bool enter_pressed = false; @@ -4069,9 +4178,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ backup_current_text_length = state->CurLenA; state->Edited = false; state->BufCapacityA = buf_size; - state->UserFlags = flags; - state->UserCallback = callback; - state->UserCallbackData = callback_user_data; + state->Flags = flags; // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Down the line we should have a cleaner library-wide concept of Selected vs Active. @@ -4083,19 +4190,49 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f)); const bool is_osx = io.ConfigMacOSXBehaviors; - if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0])) + if (select_all) { state->SelectAll(); state->SelectedAllMouseLock = true; } - else if (hovered && is_osx && io.MouseDoubleClicked[0]) + else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift) { - // Double-click select a word only, OS X style (by simulating keystrokes) - state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); - state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); + const int multiclick_count = (io.MouseClickedCount[0] - 2); + if ((multiclick_count % 2) == 0) + { + // Double-click: Select word + // We always use the "Mac" word advance for double-click select vs CTRL+Right which use the platform dependent variant: + // FIXME: There are likely many ways to improve this behavior, but there's no "right" behavior (depends on use-case, software, OS) + const bool is_bol = (state->Stb.cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor - 1) == '\n'; + if (STB_TEXT_HAS_SELECTION(&state->Stb) || !is_bol) + state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); + //state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + if (!STB_TEXT_HAS_SELECTION(&state->Stb)) + ImStb::stb_textedit_prep_selection_at_cursor(&state->Stb); + state->Stb.cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb.cursor); + state->Stb.select_end = state->Stb.cursor; + ImStb::stb_textedit_clamp(state, &state->Stb); + } + else + { + // Triple-click: Select line + const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor) == '\n'; + state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART); + state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT); + state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT); + if (!is_eol && is_multiline) + { + ImSwap(state->Stb.select_start, state->Stb.select_end); + state->Stb.cursor = state->Stb.select_end; + } + state->CursorFollow = false; + } + state->CursorAnimReset(); } else if (io.MouseClicked[0] && !state->SelectedAllMouseLock) { + // FIXME: unselect on late click could be done release? if (hovered) { stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); @@ -4126,7 +4263,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. if (io.InputQueueCharacters.Size > 0) { - if (!ignore_char_inputs && !is_readonly && !user_nav_input_start) + if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav) for (int n = 0; n < io.InputQueueCharacters.Size; n++) { // Insert character if they pass filtering @@ -4167,6 +4304,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && !is_readonly && is_undoable); const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && !is_readonly && is_undoable; + // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful. + const bool is_validate_enter = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter); + const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) && !IsKeyPressedMap(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed); + const bool is_cancel = IsKeyPressedMap(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed); + if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } @@ -4175,7 +4317,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ else if (IsKeyPressedMap(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; } else if (IsKeyPressedMap(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } else if (IsKeyPressedMap(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly && !is_cut) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } else if (IsKeyPressedMap(ImGuiKey_Backspace) && !is_readonly) { if (!state->HasSelection()) @@ -4187,7 +4329,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter)) + else if (is_validate_enter) { bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) @@ -4201,7 +4343,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->OnKeyPressed((int)c); } } - else if (IsKeyPressedMap(ImGuiKey_Escape)) + else if (is_validate_nav) + { + IM_ASSERT(!is_validate_enter); + enter_pressed = clear_active_id = true; + } + else if (is_cancel) { clear_active_id = cancel_edit = true; } @@ -4269,11 +4416,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } // Process callbacks and apply result back to user's buffer. + const char* apply_new_text = NULL; + int apply_new_text_length = 0; if (g.ActiveId == id) { IM_ASSERT(state != NULL); - const char* apply_new_text = NULL; - int apply_new_text_length = 0; if (cancel_edit) { // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. @@ -4349,8 +4496,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ callback_data.Flags = flags; callback_data.UserData = callback_user_data; + char* callback_buf = is_readonly ? buf : state->TextA.Data; callback_data.EventKey = event_key; - callback_data.Buf = state->TextA.Data; + callback_data.Buf = callback_buf; callback_data.BufTextLen = state->CurLenA; callback_data.BufSize = state->BufCapacityA; callback_data.BufDirty = false; @@ -4365,7 +4513,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ callback(&callback_data); // Read back what user may have modified - IM_ASSERT(callback_data.Buf == state->TextA.Data); // Invalid to modify those fields + callback_buf = is_readonly ? buf : state->TextA.Data; // Pointer may have been invalidated by a resize callback + IM_ASSERT(callback_data.Buf == callback_buf); // Invalid to modify those fields IM_ASSERT(callback_data.BufSize == state->BufCapacityA); IM_ASSERT(callback_data.Flags == flags); const bool buf_dirty = callback_data.BufDirty; @@ -4392,39 +4541,37 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } } - // Copy result to user buffer - if (apply_new_text) - { - // We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size - // of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used - // without any storage on user's side. - IM_ASSERT(apply_new_text_length >= 0); - if (is_resizable) - { - ImGuiInputTextCallbackData callback_data; - callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; - callback_data.Flags = flags; - callback_data.Buf = buf; - callback_data.BufTextLen = apply_new_text_length; - callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); - callback_data.UserData = callback_user_data; - callback(&callback_data); - buf = callback_data.Buf; - buf_size = callback_data.BufSize; - apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); - IM_ASSERT(apply_new_text_length <= buf_size); - } - //IMGUI_DEBUG_LOG("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length); - - // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. - ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); - value_changed = true; - } - // Clear temporary user storage - state->UserFlags = 0; - state->UserCallback = NULL; - state->UserCallbackData = NULL; + state->Flags = ImGuiInputTextFlags_None; + } + + // Copy result to user buffer. This can currently only happen when (g.ActiveId == id) + if (apply_new_text != NULL) + { + // We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size + // of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used + // without any storage on user's side. + IM_ASSERT(apply_new_text_length >= 0); + if (is_resizable) + { + ImGuiInputTextCallbackData callback_data; + callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; + callback_data.Flags = flags; + callback_data.Buf = buf; + callback_data.BufTextLen = apply_new_text_length; + callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); + callback_data.UserData = callback_user_data; + callback(&callback_data); + buf = callback_data.Buf; + buf_size = callback_data.BufSize; + apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1); + IM_ASSERT(apply_new_text_length <= buf_size); + } + //IMGUI_DEBUG_LOG("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length); + + // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. + ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); + value_changed = true; } // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) @@ -4546,7 +4693,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Test if cursor is vertically visible if (cursor_offset.y - g.FontSize < scroll_y) scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); - else if (cursor_offset.y - inner_size.y >= scroll_y) + else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y) scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f; const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f); scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y); @@ -4606,7 +4753,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { state->CursorAnim += io.DeltaTime; bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; - ImVec2 cursor_screen_pos = draw_pos + cursor_offset - draw_scroll; + ImVec2 cursor_screen_pos = ImFloor(draw_pos + cursor_offset - draw_scroll); ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); @@ -4638,9 +4785,23 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_multiline) { + // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (ref issue #4761)... Dummy(ImVec2(text_size.x, text_size.y + style.FramePadding.y)); + ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; EndChild(); + item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow); + g.CurrentItemFlags = backup_item_flags; + + // ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active... + // FIXME: This quite messy/tricky, should attempt to get rid of the child window. EndGroup(); + if (g.LastItemData.ID == 0) + { + g.LastItemData.ID = id; + g.LastItemData.InFlags = item_data_backup.InFlags; + g.LastItemData.StatusFlags = item_data_backup.StatusFlags; + } } // Log as text @@ -4656,7 +4817,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (value_changed && !(flags & ImGuiInputTextFlags_NoMarkEdited)) MarkItemEdited(id); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) return enter_pressed; else @@ -4683,6 +4844,30 @@ bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flag return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); } +// ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation. +// Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting. +static void ColorEditRestoreHS(const float* col, float* H, float* S, float* V) +{ + // This check is optional. Suppose we have two color widgets side by side, both widgets display different colors, but both colors have hue and/or saturation undefined. + // With color check: hue/saturation is preserved in one widget. Editing color in one widget would reset hue/saturation in another one. + // Without color check: common hue/saturation would be displayed in all widgets that have hue/saturation undefined. + // g.ColorEditLastColor is stored as ImU32 RGB value: this essentially gives us color equality check with reduced precision. + // Tiny external color changes would not be detected and this check would still pass. This is OK, since we only restore hue/saturation _only_ if they are undefined, + // therefore this change flipping hue/saturation from undefined to a very tiny value would still be represented in color picker. + ImGuiContext& g = *GImGui; + if (g.ColorEditLastColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) + return; + + // When S == 0, H is undefined. + // When H == 1 it wraps around to 0. + if (*S == 0.0f || (*H == 0.0f && g.ColorEditLastHue == 1)) + *H = g.ColorEditLastHue; + + // When V == 0, S is undefined. + if (*V == 0.0f) + *S = g.ColorEditLastSat; +} + // Edit colors components (each component in 0.0f..1.0f range). // See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. // With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. @@ -4707,24 +4892,24 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // If we're not showing any slider there's no point in doing any HSV conversions const ImGuiColorEditFlags flags_untouched = flags; if (flags & ImGuiColorEditFlags_NoInputs) - flags = (flags & (~ImGuiColorEditFlags__DisplayMask)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions; + flags = (flags & (~ImGuiColorEditFlags_DisplayMask_)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions; // Context menu: display and modify options (before defaults are applied) if (!(flags & ImGuiColorEditFlags_NoOptions)) ColorEditOptionsPopup(col, flags); // Read stored options - if (!(flags & ImGuiColorEditFlags__DisplayMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DisplayMask); - if (!(flags & ImGuiColorEditFlags__DataTypeMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask); - if (!(flags & ImGuiColorEditFlags__PickerMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask); - if (!(flags & ImGuiColorEditFlags__InputMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputMask); - flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__DisplayMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags__InputMask)); - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DisplayMask)); // Check that only 1 is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check that only 1 is selected + if (!(flags & ImGuiColorEditFlags_DisplayMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DisplayMask_); + if (!(flags & ImGuiColorEditFlags_DataTypeMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DataTypeMask_); + if (!(flags & ImGuiColorEditFlags_PickerMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_); + if (!(flags & ImGuiColorEditFlags_InputMask_)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_InputMask_); + flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_)); + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check that only 1 is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; @@ -4738,13 +4923,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag { // Hue is lost when converting from greyscale rgb (saturation=0). Restore it. ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) - { - if (f[1] == 0) - f[0] = g.ColorEditLastHue; - if (f[2] == 0) - f[1] = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &f[0], &f[1], &f[2]); } int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; @@ -4814,10 +4993,12 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag p++; i[0] = i[1] = i[2] = 0; i[3] = 0xFF; // alpha default to 255 is not parsed by scanf (e.g. inputting #FFFFFF omitting alpha) + int r; if (alpha) - sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) + r = sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) else - sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + r = sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + IM_UNUSED(r); // Fixes C6031: Return value ignored: 'sscanf'. } if (!(flags & ImGuiColorEditFlags_NoOptions)) OpenPopupOnItemClick("context"); @@ -4837,7 +5018,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // Store current color and open a picker g.ColorPickerRef = col_v4; OpenPopup("picker"); - SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1, style.ItemSpacing.y)); + SetNextWindowPos(g.LastItemData.Rect.GetBL() + ImVec2(-1, style.ItemSpacing.y)); } } if (!(flags & ImGuiColorEditFlags_NoOptions)) @@ -4851,8 +5032,8 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag TextEx(label, label_display_end); Spacing(); } - ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; - ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__DisplayMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; + ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); EndPopup(); @@ -4877,7 +5058,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag g.ColorEditLastHue = f[0]; g.ColorEditLastSat = f[1]; ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); - memcpy(g.ColorEditLastColor, f, sizeof(float) * 3); + g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0)); } if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV)) ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); @@ -4894,7 +5075,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // Drag and Drop Target // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. - if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) + if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) { bool accepted_drag_drop = false; if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) @@ -4916,10 +5097,10 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4(). if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) - window->DC.LastItemId = g.ActiveId; + g.LastItemData.ID = g.ActiveId; if (value_changed) - MarkItemEdited(window->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); return value_changed; } @@ -4972,12 +5153,12 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ColorPickerOptionsPopup(col, flags); // Read stored options - if (!(flags & ImGuiColorEditFlags__PickerMask)) - flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask; - if (!(flags & ImGuiColorEditFlags__InputMask)) - flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__InputMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__InputMask; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__PickerMask)); // Check that only 1 is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check that only 1 is selected + if (!(flags & ImGuiColorEditFlags_PickerMask_)) + flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_; + if (!(flags & ImGuiColorEditFlags_InputMask_)) + flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_InputMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check that only 1 is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check that only 1 is selected if (!(flags & ImGuiColorEditFlags_NoOptions)) flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); @@ -5012,13 +5193,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { // Hue is lost when converting from greyscale rgb (saturation=0). Restore it. ColorConvertRGBtoHSV(R, G, B, H, S, V); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) - { - if (S == 0) - H = g.ColorEditLastHue; - if (V == 0) - S = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &H, &S, &V); } else if (flags & ImGuiColorEditFlags_InputHSV) { @@ -5071,6 +5246,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1)); V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); + + // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square. + if (g.ColorEditLastColor == ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0))) + H = g.ColorEditLastHue; value_changed = value_changed_sv = true; } if (!(flags & ImGuiColorEditFlags_NoOptions)) @@ -5123,7 +5302,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if ((flags & ImGuiColorEditFlags_NoLabel)) Text("Current"); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip; + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip; ColorButton("##current", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)); if (ref_col != NULL) { @@ -5144,10 +5323,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { if (flags & ImGuiColorEditFlags_InputRGB) { - ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10 * 1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); + ColorConvertHSVtoRGB(H, S, V, col[0], col[1], col[2]); g.ColorEditLastHue = H; g.ColorEditLastSat = S; - memcpy(g.ColorEditLastColor, col, sizeof(float) * 3); + g.ColorEditLastColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)); } else if (flags & ImGuiColorEditFlags_InputHSV) { @@ -5162,9 +5341,9 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if ((flags & ImGuiColorEditFlags_NoInputs) == 0) { PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; - if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB)) { // FIXME: Hackily differentiating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget. @@ -5172,9 +5351,9 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap); value_changed = true; } - if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_DisplayHSV); - if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags__DisplayMask) == 0) + if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_DisplayHex); PopItemWidth(); } @@ -5201,13 +5380,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl G = col[1]; B = col[2]; ColorConvertRGBtoHSV(R, G, B, H, S, V); - if (memcmp(g.ColorEditLastColor, col, sizeof(float) * 3) == 0) // Fix local Hue as display below will use it immediately. - { - if (S == 0) - H = g.ColorEditLastHue; - if (V == 0) - S = g.ColorEditLastSat; - } + ColorEditRestoreHS(col, &H, &S, &V); // Fix local Hue as display below will use it immediately. } else if (flags & ImGuiColorEditFlags_InputHSV) { @@ -5315,7 +5488,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0) value_changed = false; if (value_changed) - MarkItemEdited(window->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); PopID(); @@ -5404,7 +5577,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl // Tooltip if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) - ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); + ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); return pressed; } @@ -5413,18 +5586,18 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) { ImGuiContext& g = *GImGui; - if ((flags & ImGuiColorEditFlags__DisplayMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DisplayMask; - if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask; - if ((flags & ImGuiColorEditFlags__PickerMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask; - if ((flags & ImGuiColorEditFlags__InputMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputMask; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DisplayMask)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__DataTypeMask)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__PickerMask)); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags__InputMask)); // Check only 1 option is selected + if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_; + if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_; + if ((flags & ImGuiColorEditFlags_PickerMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_; + if ((flags & ImGuiColorEditFlags_InputMask_) == 0) + flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_InputMask_; + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DataTypeMask_)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_)); // Check only 1 option is selected g.ColorEditOptions = flags; } @@ -5433,7 +5606,7 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags { ImGuiContext& g = *GImGui; - BeginTooltipEx(0, ImGuiTooltipFlags_OverridePreviousTooltip); + BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None); const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; if (text_end > text) { @@ -5444,9 +5617,9 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); - ColorButton("##preview", cf, (flags & (ImGuiColorEditFlags__InputMask | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); + ColorButton("##preview", cf, (flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); SameLine(); - if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags__InputMask)) + if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags_InputMask_)) { if (flags & ImGuiColorEditFlags_NoAlpha) Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); @@ -5465,23 +5638,23 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) { - bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__DisplayMask); - bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask); + bool allow_opt_inputs = !(flags & ImGuiColorEditFlags_DisplayMask_); + bool allow_opt_datatype = !(flags & ImGuiColorEditFlags_DataTypeMask_); if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) return; ImGuiContext& g = *GImGui; ImGuiColorEditFlags opts = g.ColorEditOptions; if (allow_opt_inputs) { - if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayRGB; - if (RadioButton("HSV", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayHSV; - if (RadioButton("Hex", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags__DisplayMask) | ImGuiColorEditFlags_DisplayHex; + if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayRGB; + if (RadioButton("HSV", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHSV; + if (RadioButton("Hex", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHex; } if (allow_opt_datatype) { if (allow_opt_inputs) Separator(); - if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8; - if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float; + if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Uint8; + if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Float; } if (allow_opt_inputs || allow_opt_datatype) @@ -5516,7 +5689,7 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags) { - bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask); + bool allow_opt_picker = !(flags & ImGuiColorEditFlags_PickerMask_); bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar); if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context")) return; @@ -5535,7 +5708,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; ImVec2 backup_pos = GetCursorScreenPos(); if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup - g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask); + g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_); SetCursorScreenPos(backup_pos); ImVec4 previewing_ref_col; memcpy(&previewing_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4)); @@ -5748,14 +5921,14 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l window->DC.TreeJumpToParentOnPopMask |= (1 << window->DC.TreeDepth); bool item_add = ItemAdd(interact_bb, id); - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; - window->DC.LastItemDisplayRect = frame_bb; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; + g.LastItemData.DisplayRect = frame_bb; if (!item_add) { if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushOverrideID(id); - IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; } @@ -5804,7 +5977,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l toggled = true; if (flags & ImGuiTreeNodeFlags_OpenOnArrow) toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job - if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseDoubleClicked[0]) + if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2) toggled = true; } else if (pressed && g.DragDropHoldJustPressedId == id) @@ -5814,12 +5987,12 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l toggled = true; } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open) + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open) { toggled = true; NavMoveRequestCancel(); } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? { toggled = true; NavMoveRequestCancel(); @@ -5829,7 +6002,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l { is_open = !is_open; window->DC.StateStorage->SetInt(id, is_open); - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledOpen; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen; } } if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) @@ -5837,7 +6010,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // In this branch, TreeNodeBehavior() cannot toggle the selection so this will never trigger. if (selected != was_selected) //-V547 - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render const ImU32 text_col = GetColorU32(ImGuiCol_Text); @@ -5868,8 +6041,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l { const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); - RenderNavHighlight(frame_bb, id, nav_highlight_flags); } + RenderNavHighlight(frame_bb, id, nav_highlight_flags); if (flags & ImGuiTreeNodeFlags_Bullet) RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col); else if (!is_leaf) @@ -5881,7 +6054,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushOverrideID(id); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); return is_open; } @@ -5890,7 +6063,7 @@ void ImGui::TreePush(const char* str_id) ImGuiWindow* window = GetCurrentWindow(); Indent(); window->DC.TreeDepth++; - PushID(str_id ? str_id : "#TreePush"); + PushID(str_id); } void ImGui::TreePush(const void* ptr_id) @@ -5898,7 +6071,7 @@ void ImGui::TreePush(const void* ptr_id) ImGuiWindow* window = GetCurrentWindow(); Indent(); window->DC.TreeDepth++; - PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); + PushID(ptr_id); } void ImGui::TreePushOverrideID(ImGuiID id) @@ -5907,7 +6080,7 @@ void ImGui::TreePushOverrideID(ImGuiID id) ImGuiWindow* window = g.CurrentWindow; Indent(); window->DC.TreeDepth++; - window->IDStack.push_back(id); + PushOverrideID(id); } void ImGui::TreePop() @@ -5985,14 +6158,14 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFl // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. // FIXME: CloseButton can overlap into text, need find a way to clip the text somehow. ImGuiContext& g = *GImGui; - ImGuiLastItemDataBackup last_item_backup; + ImGuiLastItemData last_item_backup = g.LastItemData; float button_size = g.FontSize; - float button_x = ImMax(window->DC.LastItemRect.Min.x, window->DC.LastItemRect.Max.x - g.Style.FramePadding.x * 2.0f - button_size); - float button_y = window->DC.LastItemRect.Min.y; + float button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x * 2.0f - button_size); + float button_y = g.LastItemData.Rect.Min.y; ImGuiID close_button_id = GetIDWithSeed("#CLOSE", NULL, id); if (CloseButton(close_button_id, ImVec2(button_x, button_y))) *p_visible = false; - last_item_backup.Restore(); + g.LastItemData = last_item_backup; } return is_open; @@ -6061,19 +6234,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl window->ClipRect.Max.x = window->ParentWorkRect.Max.x; } - bool item_add; - if (flags & ImGuiSelectableFlags_Disabled) - { - ImGuiItemFlags backup_item_flags = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus; - item_add = ItemAdd(bb, id); - window->DC.ItemFlags = backup_item_flags; - } - else - { - item_add = ItemAdd(bb, id); - } - + const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0; + const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? ImGuiItemFlags_Disabled : ImGuiItemFlags_None); if (span_all_columns) { window->ClipRect.Min.x = backup_clip_rect_min_x; @@ -6083,6 +6245,10 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (!item_add) return false; + const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; + if (disabled_item && !disabled_global) // Only testing this as an optimization + BeginDisabled(); + // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only, // which would be advantageous since most selectable are not selected. if (span_all_columns && window->DC.CurrentColumns) @@ -6095,23 +6261,30 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; } if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; } if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; } - if (flags & ImGuiSelectableFlags_Disabled) { button_flags |= ImGuiButtonFlags_Disabled; } if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; } - if (flags & ImGuiSelectableFlags_Disabled) - selected = false; - const bool was_selected = selected; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + // Auto-select when moved into + // - This will be more fully fleshed in the range-select branch + // - This is not exposed as it won't nicely work with some user side handling of shift/control + // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons + // - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope()) + // - (2) usage will fail with clipped items + // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. + if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == window->DC.NavFocusScopeIdCurrent) + if (g.NavJustMovedToId == id) + selected = pressed = true; + // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) { if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) { - SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, ImRect(bb.Min - window->Pos, bb.Max - window->Pos)); + SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, WindowRectAbsToRel(window, bb)); // (bb == NavRect) g.NavDisableHighlight = true; } } @@ -6123,7 +6296,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // In this branch, Selectable() cannot toggle the selection so this will never trigger. if (selected != was_selected) //-V547 - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render if (held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld)) @@ -6132,24 +6305,25 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl { const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(bb.Min, bb.Max, col, false, 0.0f); - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); } + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); if (span_all_columns && window->DC.CurrentColumns) PopColumnsBackground(); else if (span_all_columns && g.CurrentTable) TablePopBackgroundChannel(); - if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); - if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); // Automatically close popups - if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.LastItemData.InFlags & ImGuiItemFlags_SelectableDontClosePopup)) CloseCurrentPopup(); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags); - return pressed; + if (disabled_item && !disabled_global) + EndDisabled(); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); + return pressed; //-V1020 } bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) @@ -6281,8 +6455,9 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v PopID(); } EndListBox(); + if (value_changed) - MarkItemEdited(g.CurrentWindow->DC.LastItemId); + MarkItemEdited(g.LastItemData.ID); return value_changed; } @@ -6297,7 +6472,7 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v // Plot/Graph widgets are not very good. // Consider writing your own, or using a third-party one, see: // - ImPlot https://github.com/epezent/implot -// - others https://github.com/ocornut/imgui/wiki/Useful-Widgets +// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions //------------------------------------------------------------------------- int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size) @@ -6374,7 +6549,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get float v0 = values_getter(data, (0 + values_offset) % values_count); float t0 = 0.0f; ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle - float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands + float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (1 + scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); @@ -6501,42 +6676,51 @@ void ImGui::Value(const char* prefix, float v, const char* float_format) // - EndMainMenuBar() // - BeginMenu() // - EndMenu() +// - MenuItemEx() [Internal] // - MenuItem() //------------------------------------------------------------------------- // Helpers for internal use -void ImGuiMenuColumns::Update(int count, float spacing, bool clear) +void ImGuiMenuColumns::Update(float spacing, bool window_reappearing) { - IM_ASSERT(count == IM_ARRAYSIZE(Pos)); - IM_UNUSED(count); - Width = NextWidth = 0.0f; - Spacing = spacing; - if (clear) - memset(NextWidths, 0, sizeof(NextWidths)); - for (int i = 0; i < IM_ARRAYSIZE(Pos); i++) + if (window_reappearing) + memset(Widths, 0, sizeof(Widths)); + Spacing = (ImU16)spacing; + CalcNextTotalWidth(true); + memset(Widths, 0, sizeof(Widths)); + TotalWidth = NextTotalWidth; + NextTotalWidth = 0; +} + +void ImGuiMenuColumns::CalcNextTotalWidth(bool update_offsets) +{ + ImU16 offset = 0; + bool want_spacing = false; + for (int i = 0; i < IM_ARRAYSIZE(Widths); i++) { - if (i > 0 && NextWidths[i] > 0.0f) - Width += Spacing; - Pos[i] = IM_FLOOR(Width); - Width += NextWidths[i]; - NextWidths[i] = 0.0f; + ImU16 width = Widths[i]; + if (want_spacing && width > 0) + offset += Spacing; + want_spacing |= (width > 0); + if (update_offsets) + { + if (i == 1) { OffsetLabel = offset; } + if (i == 2) { OffsetShortcut = offset; } + if (i == 3) { OffsetMark = offset; } + } + offset += width; } + NextTotalWidth = offset; } -float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double +float ImGuiMenuColumns::DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark) { - NextWidth = 0.0f; - NextWidths[0] = ImMax(NextWidths[0], w0); - NextWidths[1] = ImMax(NextWidths[1], w1); - NextWidths[2] = ImMax(NextWidths[2], w2); - for (int i = 0; i < IM_ARRAYSIZE(Pos); i++) - NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f); - return ImMax(Width, NextWidth); -} - -float ImGuiMenuColumns::CalcExtraSpace(float avail_w) const -{ - return ImMax(0.0f, avail_w - Width); + Widths[0] = ImMax(Widths[0], (ImU16)w_icon); + Widths[1] = ImMax(Widths[1], (ImU16)w_label); + Widths[2] = ImMax(Widths[2], (ImU16)w_shortcut); + Widths[3] = ImMax(Widths[3], (ImU16)w_mark); + CalcNextTotalWidth(false); + return (float)ImMax(TotalWidth, NextTotalWidth); } // FIXME: Provided a rectangle perhaps e.g. a BeginMenuBarEx() could be used anywhere.. @@ -6581,24 +6765,25 @@ void ImGui::EndMenuBar() // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) { + // Try to find out if the request is for one of our child menu ImGuiWindow* nav_earliest_child = g.NavWindow; while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) nav_earliest_child = nav_earliest_child->ParentWindow; - if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None) + if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) { // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. - // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) + // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering) const ImGuiNavLayer layer = ImGuiNavLayer_Menu; - IM_ASSERT(window->DC.NavLayerActiveMaskNext & (1 << layer)); // Sanity check + IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check FocusWindow(window); SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. g.NavDisableMouseHover = g.NavMousePosDirty = true; - g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; - NavMoveRequestCancel(); + NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat } } + IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); IM_ASSERT(window->DC.MenuBarAppending); PopClipRect(); @@ -6683,7 +6868,24 @@ void ImGui::EndMainMenuBar() End(); } -bool ImGui::BeginMenu(const char* label, bool enabled) +static bool IsRootOfOpenMenuSet() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu)) + return false; + + // Initially we used 'OpenParentId' to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) based on parent ID. + // This would however prevent the use of e.g. PuhsID() user code submitting menus. + // Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag, + // making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects. + // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup + // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first chilld menu. + const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size]; + return (/*upper_popup->OpenParentId == window->IDStack.back() &&*/ upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu)); +} + +bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -6695,8 +6897,9 @@ bool ImGui::BeginMenu(const char* label, bool enabled) bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None); // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) + // The first menu in a hierarchy isn't so hovering doesn't get accross (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation. ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; - if (window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) + if (window->Flags & ImGuiWindowFlags_ChildMenu) flags |= ImGuiWindowFlags_ChildWindow; // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). @@ -6715,16 +6918,22 @@ bool ImGui::BeginMenu(const char* label, bool enabled) g.MenusIdSubmittedThisFrame.push_back(id); ImVec2 label_size = CalcTextSize(label, NULL, true); - bool pressed; - bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].OpenParentId == window->IDStack.back()); + + // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window) + const bool menuset_is_open = IsRootOfOpenMenuSet(); ImGuiWindow* backed_nav_window = g.NavWindow; if (menuset_is_open) - g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) + g.NavWindow = window; // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering. ImVec2 popup_pos, pos = window->DC.CursorPos; + PushID(label); + if (!enabled) + BeginDisabled(); + const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; + bool pressed; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Menu inside an horizontal menu bar @@ -6734,24 +6943,33 @@ bool ImGui::BeginMenu(const char* label, bool enabled) window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); float w = label_size.x; - pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); + ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + pressed = Selectable("", menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups, ImVec2(w, 0.0f)); + RenderText(text_pos, label); PopStyleVar(); window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). } else { - // Menu inside a menu + // Menu inside a regular/vertical menu // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); - float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, 0.0f, IM_FLOOR(g.FontSize * 1.20f)); // Feedback to next frame + float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; + float checkmark_w = IM_FLOOR(g.FontSize * 1.20f); + float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); - pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_SpanAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(min_w, 0.0f)); - ImU32 text_col = GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled); - RenderArrow(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), text_col, ImGuiDir_Right); + ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + pressed = Selectable("", menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + RenderText(text_pos, label); + if (icon_w > 0.0f) + RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); + RenderArrow(window->DrawList, pos + ImVec2(offsets->OffsetMark + extra_w + g.FontSize * 0.30f, 0.0f), GetColorU32(ImGuiCol_Text), ImGuiDir_Right); } + if (!enabled) + EndDisabled(); - const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id); + const bool hovered = (g.HoveredId == id) && enabled; if (menuset_is_open) g.NavWindow = backed_nav_window; @@ -6762,36 +6980,30 @@ bool ImGui::BeginMenu(const char* label, bool enabled) // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. bool moving_toward_other_child_menu = false; - ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? g.OpenPopupStack[g.BeginPopupStack.Size].Window : NULL; if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar)) { - // FIXME-DPI: Values should be derived from a master "scale" factor. + float ref_unit = g.FontSize; // FIXME-DPI ImRect next_window_rect = child_menu_window->Rect(); - ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; + ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta); ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); - float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. - ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues - tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? - tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); + float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // add a bit of extra slack. + ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues (FIXME: ??) + tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? + tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +ref_unit * 8.0f); moving_toward_other_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); - //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] + //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_other_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] } if (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_toward_other_child_menu) want_close = true; - if (!menu_is_open && hovered && pressed) // Click to open + // Open + if (!menu_is_open && pressed) // Click/activate to open want_open = true; else if (!menu_is_open && hovered && !moving_toward_other_child_menu) // Hover to open want_open = true; - - if (g.NavActivateId == id) - { - want_close = menu_is_open; - want_open = !menu_is_open; - } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open + if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open { want_open = true; NavMoveRequestCancel(); @@ -6809,7 +7021,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) { want_open = true; } - else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open + else if (g.NavId == id && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open { want_open = true; NavMoveRequestCancel(); @@ -6821,7 +7033,8 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (want_close && IsPopupOpen(id, ImGuiPopupFlags_None)) ClosePopupToLevel(g.BeginPopupStack.Size, true); - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); + PopID(); if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) { @@ -6837,7 +7050,9 @@ bool ImGui::BeginMenu(const char* label, bool enabled) if (menu_is_open) { SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: this is super misleading! The value will serve as reference for FindBestWindowPosForPopup(), not actual pos. + PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + PopStyleVar(); } else { @@ -6847,6 +7062,11 @@ bool ImGui::BeginMenu(const char* label, bool enabled) return menu_is_open; } +bool ImGui::BeginMenu(const char* label, bool enabled) +{ + return BeginMenuEx(label, NULL, enabled); +} + void ImGui::EndMenu() { // Nav: When a left move request _within our child menu_ failed, close ourselves (the _parent_ menu). @@ -6854,16 +7074,17 @@ void ImGui::EndMenu() // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction. ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) - { - ClosePopupToLevel(g.BeginPopupStack.Size, true); - NavMoveRequestCancel(); - } + if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) + if (g.NavWindow && (g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow == window) + { + ClosePopupToLevel(g.BeginPopupStack.Size, true); + NavMoveRequestCancel(); + } EndPopup(); } -bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) +bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut, bool selected, bool enabled) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -6874,19 +7095,31 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo ImVec2 pos = window->DC.CursorPos; ImVec2 label_size = CalcTextSize(label, NULL, true); + const bool menuset_is_open = IsRootOfOpenMenuSet(); + ImGuiWindow* backed_nav_window = g.NavWindow; + if (menuset_is_open) + g.NavWindow = window; + // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. - ImGuiSelectableFlags flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover | (enabled ? 0 : ImGuiSelectableFlags_Disabled); bool pressed; + PushID(label); + if (!enabled) + BeginDisabled(); + + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover; + const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful - // Note that in this situation we render neither the shortcut neither the selected tick mark + // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark. float w = label_size.x; window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); + ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); - pressed = Selectable(label, false, flags, ImVec2(w, 0.0f)); + pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f)); PopStyleVar(); + RenderText(text_pos, label); window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). } else @@ -6894,27 +7127,42 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo // Menu item inside a vertical menu // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f. // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. - float shortcut_w = shortcut ? CalcTextSize(shortcut, NULL).x : 0.0f; - float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, shortcut_w, IM_FLOOR(g.FontSize * 1.20f)); // Feedback for next frame - float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); - pressed = Selectable(label, false, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; + float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f; + float checkmark_w = IM_FLOOR(g.FontSize * 1.20f); + float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame + float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); + pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); + RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label); + if (icon_w > 0.0f) + RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); if (shortcut_w > 0.0f) { - PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - RenderText(pos + ImVec2(window->DC.MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false); + PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); + RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false); PopStyleColor(); } if (selected) - RenderCheckMark(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f); + RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); } + IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); + if (!enabled) + EndDisabled(); + PopID(); + if (menuset_is_open) + g.NavWindow = backed_nav_window; - IMGUI_TEST_ENGINE_ITEM_INFO(window->DC.LastItemId, label, window->DC.ItemFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); return pressed; } +bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) +{ + return MenuItemEx(label, NULL, shortcut, selected, enabled); +} + bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) { - if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled)) + if (MenuItemEx(label, NULL, shortcut, p_selected ? *p_selected : false, enabled)) { if (p_selected) *p_selected = !*p_selected; @@ -7045,8 +7293,7 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG // Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable))) - if (tab_bar->Tabs.Size > 1) - ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); + ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); tab_bar->TabsAddedNew = false; // Flags @@ -7230,6 +7477,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) curr_section_n = section_n; // Store data so we can build an array sorted by width if we need to shrink tabs down + IM_MSVC_WARNING_SUPPRESS(6385); int shrink_buffer_index = shrink_buffer_indexes[section_n]++; g.ShrinkWidthBuffer[shrink_buffer_index].Index = tab_n; g.ShrinkWidthBuffer[shrink_buffer_index].Width = tab->ContentWidth; @@ -7715,12 +7963,10 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // If the user called us with *p_open == false, we early out and don't render. // We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID. - IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); if (p_open && !*p_open) { - PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); - ItemAdd(ImRect(), id); - PopItemFlag(); + ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus); return false; } @@ -7760,7 +8006,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, tab->Flags = flags; // Append name with zero-terminator - tab->NameOffset = (ImS16)tab_bar->TabsNames.size(); + tab->NameOffset = (ImS32)tab_bar->TabsNames.size(); tab_bar->TabsNames.append(label, label + strlen(label) + 1); // Update selected tab @@ -7787,9 +8033,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'. if (tab_appearing && (!tab_bar_appearing || tab_is_new)) { - PushItemFlag(ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus, true); - ItemAdd(ImRect(), id); - PopItemFlag(); + ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus); if (is_tab_button) return false; return tab_contents_visible; @@ -7836,7 +8080,6 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); if (pressed && !is_tab_button) tab_bar->NextSelectedTabId = id; - hovered |= (g.HoveredId == id); // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) if (g.ActiveId != id) @@ -7900,8 +8143,11 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, PopClipRect(); window->DC.CursorPos = backup_main_cursor_pos; - // Tooltip (FIXME: Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer) - // We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar (which g.HoveredId ignores) + // Tooltip + // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok) + // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores) + // FIXME: This is a mess. + // FIXME: We may want disabled tab to still display the tooltip? if (text_clipped && g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > g.TooltipSlowDelay && IsItemHovered()) if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); @@ -7989,14 +8235,7 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, #endif // Render text label (with clipping + alpha gradient) + unsaved marker - const char* TAB_UNSAVED_MARKER = "*"; ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y); - if (flags & ImGuiTabItemFlags_UnsavedDocument) - { - text_pixel_clip_bb.Max.x -= CalcTextSize(TAB_UNSAVED_MARKER, NULL, false).x; - ImVec2 unsaved_marker_pos(ImMin(bb.Min.x + frame_padding.x + label_size.x + 2, text_pixel_clip_bb.Max.x), bb.Min.y + frame_padding.y + IM_FLOOR(-g.FontSize * 0.25f)); - RenderTextClippedEx(draw_list, unsaved_marker_pos, bb.Max - frame_padding, TAB_UNSAVED_MARKER, NULL, NULL); - } ImRect text_ellipsis_clip_bb = text_pixel_clip_bb; // Return clipped state ignoring the close button @@ -8006,7 +8245,10 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, //draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255)); } - // Close Button + const float button_sz = g.FontSize; + const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x * 2.0f - button_sz), bb.Min.y); + + // Close Button & Unsaved Marker // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap() // 'hovered' will be true when hovering the Tab but NOT when hovering the close button // 'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button @@ -8014,28 +8256,40 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, bool close_button_pressed = false; bool close_button_visible = false; if (close_button_id != 0) - if (is_contents_visible || bb.GetWidth() >= g.Style.TabMinWidthForCloseButton) + if (is_contents_visible || bb.GetWidth() >= ImMax(button_sz, g.Style.TabMinWidthForCloseButton)) if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id) close_button_visible = true; + bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x); + if (close_button_visible) { - ImGuiLastItemDataBackup last_item_backup; - const float close_button_sz = g.FontSize; + ImGuiLastItemData last_item_backup = g.LastItemData; PushStyleVar(ImGuiStyleVar_FramePadding, frame_padding); - if (CloseButton(close_button_id, ImVec2(bb.Max.x - frame_padding.x * 2.0f - close_button_sz, bb.Min.y))) + if (CloseButton(close_button_id, button_pos)) close_button_pressed = true; PopStyleVar(); - last_item_backup.Restore(); + g.LastItemData = last_item_backup; // Close with middle mouse button if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2)) close_button_pressed = true; - - text_pixel_clip_bb.Max.x -= close_button_sz; + } + else if (unsaved_marker_visible) + { + const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz) + g.Style.FramePadding * 2.0f); + RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text)); } + // This is all rather complicated + // (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position) // FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist.. float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb.Max.x : bb.Max.x - 1.0f; + if (close_button_visible || unsaved_marker_visible) + { + text_pixel_clip_bb.Max.x -= close_button_visible ? (button_sz) : (button_sz * 0.80f); + text_ellipsis_clip_bb.Max.x -= unsaved_marker_visible ? (button_sz * 0.80f) : 0.0f; + ellipsis_max_x = text_pixel_clip_bb.Max.x; + } RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size); #if 0 @@ -8047,7 +8301,6 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, *out_just_closed = close_button_pressed; } - const char* const KeyNames[] = { "Null", @@ -8356,7 +8609,7 @@ bool ImGui::Hotkey(const char* label, int* key, const ImVec2& ssize) ImGui::SetActiveID(id, window); ImGui::FocusWindow(window); } - else if (io.MouseClicked[0]) + else if (io.MouseClicked[0]) { if (g.ActiveId == id) { diff --git a/r5dev/thirdparty/lzham/include/lzham.h b/r5dev/thirdparty/lzham/include/lzham.h new file mode 100644 index 00000000..526c0ab7 --- /dev/null +++ b/r5dev/thirdparty/lzham/include/lzham.h @@ -0,0 +1,765 @@ +// File: lzham.h - Copyright (c) 2009-2012 Richard Geldreich, Jr. +// LZHAM uses the MIT License. See Copyright Notice and license at the end of this file. +// +// This is the main header file, includable from C or C++ files, which defines all the publically available API's, structs, and types used by the LZHAM codec. +// +// Notes: +// +// As of LZHAM alpha8, there are now two sets of API's: +// - The first (oldest) API directly exposes all of the codec's functionality. See lzham_compress_init(), lzham_decompress_init(), etc. This API has the lowest overhead +// and is the most tested. +// - The new API implements the most useful/popular subset of the zlib API, but doesn't expose all of the codec's functionality yet. See the lzham_z* functions. +// This functionality is provided because most users of compression libraries are already very familiar with the nuts and bolts of the zlib API. +// For the most common zlib usage cases LZHAM is an almost drop-in replacement for zlib. To make switching from zlib even easier, you can define the LZHAM_DEFINE_ZLIB_API macro, +// which causes this header to #define most zlib symbols to their LZHAM equivalents. +// Note that LZHAM does not actually implement the deflate/inflate algorithm, so it cannot decompress streams created by standard zlib yet (and of course, zlib cannot decompress +// streams created by LZHAM). Internally, this API is mostly implemented via the older low-level LZHAM API. + +#ifndef __LZHAM_H__ +#define __LZHAM_H__ + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +// Upper byte = major version +// Lower byte = minor version +#define LZHAM_DLL_VERSION 0x1008 + +#if defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) + #define LZHAM_64BIT 1 +#endif + +#if defined(_MSC_VER) + #define LZHAM_CDECL __cdecl +#else + #define LZHAM_CDECL +#endif + +#ifdef LZHAM_EXPORTS + #define LZHAM_DLL_EXPORT __declspec(dllexport) +#else + #define LZHAM_DLL_EXPORT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + typedef unsigned char lzham_uint8; + typedef signed int lzham_int32; + typedef unsigned int lzham_uint32; + typedef unsigned int lzham_bool; + + // Returns DLL version (LZHAM_DLL_VERSION). + LZHAM_DLL_EXPORT lzham_uint32 LZHAM_CDECL lzham_get_version(void); + + // User provided memory allocation + + // Custom allocation function must return pointers with LZHAM_MIN_ALLOC_ALIGNMENT (or better). + #define LZHAM_MIN_ALLOC_ALIGNMENT sizeof(size_t) * 2 + + typedef void* (LZHAM_CDECL *lzham_realloc_func)(void* p, size_t size, size_t* pActual_size, lzham_bool movable, void* pUser_data); + typedef size_t (LZHAM_CDECL *lzham_msize_func)(void* p, void* pUser_data); + + // Call this function to force LZHAM to use custom memory malloc(), realloc(), free() and msize functions. + LZHAM_DLL_EXPORT void LZHAM_CDECL lzham_set_memory_callbacks(lzham_realloc_func pRealloc, lzham_msize_func pMSize, void* pUser_data); + + // lzham_flush_t must map directly to the zlib-style API flush types (LZHAM_Z_NO_FLUSH, etc.) + typedef enum + { + LZHAM_NO_FLUSH = 0, + LZHAM_SYNC_FLUSH = 2, + LZHAM_FULL_FLUSH = 3, + LZHAM_FINISH = 4, + LZHAM_TABLE_FLUSH = 10 + } lzham_flush_t; + + // Compression + #define LZHAM_MIN_DICT_SIZE_LOG2 15 + #define LZHAM_MAX_DICT_SIZE_LOG2_X86 26 + #define LZHAM_MAX_DICT_SIZE_LOG2_X64 29 + + #define LZHAM_MAX_HELPER_THREADS 64 + + typedef enum + { + LZHAM_COMP_STATUS_NOT_FINISHED = 0, + LZHAM_COMP_STATUS_NEEDS_MORE_INPUT, + LZHAM_COMP_STATUS_HAS_MORE_OUTPUT, + + // All the following enums must indicate failure/success. + + LZHAM_COMP_STATUS_FIRST_SUCCESS_OR_FAILURE_CODE, + LZHAM_COMP_STATUS_SUCCESS = LZHAM_COMP_STATUS_FIRST_SUCCESS_OR_FAILURE_CODE, + + LZHAM_COMP_STATUS_FIRST_FAILURE_CODE, + LZHAM_COMP_STATUS_FAILED = LZHAM_COMP_STATUS_FIRST_FAILURE_CODE, + LZHAM_COMP_STATUS_FAILED_INITIALIZING, + LZHAM_COMP_STATUS_INVALID_PARAMETER, + LZHAM_COMP_STATUS_OUTPUT_BUF_TOO_SMALL, + + LZHAM_COMP_STATUS_FORCE_DWORD = 0xFFFFFFFF + } lzham_compress_status_t; + + typedef enum + { + LZHAM_COMP_LEVEL_FASTEST = 0, + LZHAM_COMP_LEVEL_FASTER, + LZHAM_COMP_LEVEL_DEFAULT, + LZHAM_COMP_LEVEL_BETTER, + LZHAM_COMP_LEVEL_UBER, + + LZHAM_TOTAL_COMP_LEVELS, + + LZHAM_COMP_LEVEL_FORCE_DWORD = 0xFFFFFFFF + } lzham_compress_level; + + // Streaming compression + typedef void *lzham_compress_state_ptr; + + typedef enum + { + LZHAM_COMP_FLAG_FORCE_POLAR_CODING = 1, // Forces Polar codes vs. Huffman, for a slight increase in decompression speed. + LZHAM_COMP_FLAG_EXTREME_PARSING = 2, // Improves ratio by allowing the compressor's parse graph to grow "higher" (up to 4 parent nodes per output node), but is much slower. + LZHAM_COMP_FLAG_DETERMINISTIC_PARSING = 4, // Guarantees that the compressed output will always be the same given the same input and parameters (no variation between runs due to kernel threading scheduling). + + // If enabled, the compressor is free to use any optimizations which could lower the decompression rate (such + // as adaptively resetting the Huffman table update rate to maximum frequency, which is costly for the decompressor). + LZHAM_COMP_FLAG_TRADEOFF_DECOMPRESSION_RATE_FOR_COMP_RATIO = 16, + + LZHAM_COMP_FLAG_WRITE_ZLIB_STREAM = 32, + } lzham_compress_flags; + + typedef struct + { + lzham_uint32 m_struct_size; // set to sizeof(lzham_compress_params) + lzham_uint32 m_dict_size_log2; // set to the log2(dictionary_size), must range between [LZHAM_MIN_DICT_SIZE_LOG2, LZHAM_MAX_DICT_SIZE_LOG2_X86] for x86 LZHAM_MAX_DICT_SIZE_LOG2_X64 for x64 + lzham_compress_level m_level; // set to LZHAM_COMP_LEVEL_FASTEST, etc. + lzham_int32 m_max_helper_threads; // max # of additional "helper" threads to create, must range between [-1,LZHAM_MAX_HELPER_THREADS], where -1=max practical + lzham_uint32 m_cpucache_total_lines; // set to 0 (optimize compressed stream to avoid L1/L2 cache misses - not currently supported) + lzham_uint32 m_cpucache_line_size; // set to 0 + lzham_uint32 m_compress_flags; // optional compression flags (see lzham_compress_flags enum) + lzham_uint32 m_num_seed_bytes; // for delta compression (optional) - number of seed bytes pointed to by m_pSeed_bytes + const void *m_pSeed_bytes; // for delta compression (optional) - pointer to seed bytes buffer, must be at least m_num_seed_bytes long + } lzham_compress_params; + + typedef struct + { + lzham_uint32 adler32; + lzham_uint32 crc32; + } lzham_compress_checksums; + + // Initializes a compressor. Returns a pointer to the compressor's internal state, or NULL on failure. + // pParams cannot be NULL. Be sure to initialize the pParams->m_struct_size member to sizeof(lzham_compress_params) (along with the other members to reasonable values) before calling this function. + // TODO: With large dictionaries this function could take a while (due to memory allocation). I need to add a reinit() API for compression (decompression already has one). + LZHAM_DLL_EXPORT lzham_compress_state_ptr LZHAM_CDECL lzham_compress_init(const lzham_compress_params *pParams); + + LZHAM_DLL_EXPORT lzham_compress_state_ptr LZHAM_CDECL lzham_compress_reinit(lzham_compress_state_ptr pState); + + // Deinitializes a compressor, releasing all allocated memory. + // returns adler32 and crc32 of source data (valid only on success). + LZHAM_DLL_EXPORT lzham_compress_checksums* LZHAM_CDECL lzham_compress_deinit(lzham_compress_state_ptr pState); + + // Compresses an arbitrarily sized block of data, writing as much available compressed data as possible to the output buffer. + // This method may be called as many times as needed, but for best perf. try not to call it with tiny buffers. + // pState - Pointer to internal compression state, created by lzham_compress_init. + // pIn_buf, pIn_buf_size - Pointer to input data buffer, and pointer to a size_t containing the number of bytes available in this buffer. + // On return, *pIn_buf_size will be set to the number of bytes read from the buffer. + // pOut_buf, pOut_buf_size - Pointer to the output data buffer, and a pointer to a size_t containing the max number of bytes that can be written to this buffer. + // On return, *pOut_buf_size will be set to the number of bytes written to this buffer. + // no_more_input_bytes_flag - Set to true to indicate that no more input bytes are available to compress (EOF). Once you call this function with this param set to true, it must stay set to true in all future calls. + // + // Normal return status codes: + // LZHAM_COMP_STATUS_NOT_FINISHED - Compression can continue, but the compressor needs more input, or it needs more room in the output buffer. + // LZHAM_COMP_STATUS_NEEDS_MORE_INPUT - Compression can contintue, but the compressor has no more output, and has no input but we're not at EOF. Supply more input to continue. + // Success/failure return status codes: + // LZHAM_COMP_STATUS_SUCCESS - Compression has completed successfully. + // LZHAM_COMP_STATUS_FAILED, LZHAM_COMP_STATUS_FAILED_INITIALIZING, LZHAM_COMP_STATUS_INVALID_PARAMETER - Something went wrong. + LZHAM_DLL_EXPORT lzham_compress_status_t LZHAM_CDECL lzham_compress( + lzham_compress_state_ptr pState, + const lzham_uint8 *pIn_buf, size_t *pIn_buf_size, + lzham_uint8 *pOut_buf, size_t *pOut_buf_size, + lzham_bool no_more_input_bytes_flag); + + LZHAM_DLL_EXPORT lzham_compress_status_t LZHAM_CDECL lzham_compress2( + lzham_compress_state_ptr pState, + const lzham_uint8 *pIn_buf, size_t *pIn_buf_size, + lzham_uint8 *pOut_buf, size_t *pOut_buf_size, + lzham_flush_t flush_type); + + // Single function call compression interface. + // Same return codes as lzham_compress, except this function can also return LZHAM_COMP_STATUS_OUTPUT_BUF_TOO_SMALL. + LZHAM_DLL_EXPORT lzham_compress_status_t LZHAM_CDECL lzham_compress_memory( + const lzham_compress_params *pParams, + lzham_uint8* pDst_buf, + size_t *pDst_len, + const lzham_uint8* pSrc_buf, + size_t src_len, + lzham_uint32 *pAdler32, + lzham_uint32 *pCrc32); + + // Decompression + typedef enum + { + // LZHAM_DECOMP_STATUS_NOT_FINISHED indicates that the decompressor is flushing its internal buffer to the caller's output buffer. + // There may be more bytes available to decompress on the next call, but there is no guarantee. + LZHAM_DECOMP_STATUS_NOT_FINISHED = 0, + + // LZHAM_DECOMP_STATUS_HAS_MORE_OUTPUT indicates that the decompressor is trying to flush its internal buffer to the caller's output buffer, + // but the caller hasn't provided any space to copy this data to the caller's output buffer. Call the lzham_decompress() again with a non-empty sized output buffer. + LZHAM_DECOMP_STATUS_HAS_MORE_OUTPUT, + + // LZHAM_DECOMP_STATUS_NEEDS_MORE_INPUT indicates that the decompressor has consumed all input bytes, has not encountered an "end of stream" code, + // and the caller hasn't set no_more_input_bytes_flag to true, so it's expecting more input to proceed. + LZHAM_DECOMP_STATUS_NEEDS_MORE_INPUT, + + // All the following enums always (and MUST) indicate failure/success. + LZHAM_DECOMP_STATUS_FIRST_SUCCESS_OR_FAILURE_CODE, + + // LZHAM_DECOMP_STATUS_SUCCESS indicates decompression has successfully completed. + LZHAM_DECOMP_STATUS_SUCCESS = LZHAM_DECOMP_STATUS_FIRST_SUCCESS_OR_FAILURE_CODE, + + // The remaining status codes indicate a failure of some sort. Most failures are unrecoverable. TODO: Document which codes are recoverable. + LZHAM_DECOMP_STATUS_FIRST_FAILURE_CODE, + + LZHAM_DECOMP_STATUS_FAILED_INITIALIZING = LZHAM_DECOMP_STATUS_FIRST_FAILURE_CODE, + LZHAM_DECOMP_STATUS_FAILED_DEST_BUF_TOO_SMALL, + LZHAM_DECOMP_STATUS_FAILED_EXPECTED_MORE_RAW_BYTES, + LZHAM_DECOMP_STATUS_FAILED_BAD_CODE, + LZHAM_DECOMP_STATUS_FAILED_ADLER32, + LZHAM_DECOMP_STATUS_FAILED_CRC32, + LZHAM_DECOMP_STATUS_FAILED_BAD_RAW_BLOCK, + LZHAM_DECOMP_STATUS_FAILED_BAD_COMP_BLOCK_SYNC_CHECK, + LZHAM_DECOMP_STATUS_FAILED_BAD_ZLIB_HEADER, + LZHAM_DECOMP_STATUS_FAILED_NEED_SEED_BYTES, + LZHAM_DECOMP_STATUS_FAILED_BAD_SEED_BYTES, + LZHAM_DECOMP_STATUS_FAILED_BAD_SYNC_BLOCK, + LZHAM_DECOMP_STATUS_INVALID_PARAMETER, + } lzham_decompress_status_t; + + typedef void *lzham_decompress_state_ptr; + + typedef enum + { + LZHAM_DECOMP_FLAG_NONE = 0, + LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED = 1 << 0, + LZHAM_DECOMP_FLAG_COMPUTE_ADLER32 = 1 << 1, + LZHAM_DECOMP_FLAG_COMPUTE_CRC32 = 1 << 2, + LZHAM_DECOMP_FLAG_READ_ZLIB_STREAM = 1 << 3, + } lzham_decompress_flags; + + // Decompression parameters structure. + // Notes: + // m_dict_size_log2 MUST match the value used during compression! + // If m_num_seed_bytes != 0, LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED must not be set (i.e. static "seed" dictionaries are not compatible with unbuffered decompression). + // The seed buffer's contents and size must match the seed buffer used during compression. + typedef struct + { + lzham_uint32 m_struct_size; // set to sizeof(lzham_decompress_params) + lzham_uint32 m_dict_size_log2; // set to the log2(dictionary_size), must range between [LZHAM_MIN_DICT_SIZE_LOG2, LZHAM_MAX_DICT_SIZE_LOG2_X86] for x86 LZHAM_MAX_DICT_SIZE_LOG2_X64 for x64 + lzham_uint32 m_decompress_flags; // optional decompression flags (see lzham_decompress_flags enum) + lzham_uint32 m_num_seed_bytes; // for delta compression (optional) - number of seed bytes pointed to by m_pSeed_bytes + const void *m_pSeed_bytes; // for delta compression (optional) - pointer to seed bytes buffer, must be at least m_num_seed_bytes long + } lzham_decompress_params; + + typedef struct + { + lzham_uint32 adler32; + lzham_uint32 crc32; + } lzham_decompress_checksums; + + // Initializes a decompressor. + // pParams cannot be NULL. Be sure to initialize the pParams->m_struct_size member to sizeof(lzham_decompress_params) (along with the other members to reasonable values) before calling this function. + // Note: With large dictionaries this function could take a while (due to memory allocation). To serially decompress multiple streams, it's faster to init a compressor once and + // reuse it using by calling lzham_decompress_reinit(). + LZHAM_DLL_EXPORT lzham_decompress_state_ptr LZHAM_CDECL lzham_decompress_init(const lzham_decompress_params *pParams); + + // Quickly re-initializes the decompressor to its initial state given an already allocated/initialized state (doesn't do any memory alloc unless necessary). + LZHAM_DLL_EXPORT lzham_decompress_state_ptr LZHAM_CDECL lzham_decompress_reinit(lzham_decompress_state_ptr pState, const lzham_decompress_params *pParams); + + // Deinitializes a decompressor. + // returns adler32 of decompressed data if compute_adler32 was true, otherwise it returns the adler32 from the compressed stream. + LZHAM_DLL_EXPORT lzham_decompress_checksums* LZHAM_CDECL lzham_decompress_deinit(lzham_decompress_state_ptr pState); + + // Decompresses an arbitrarily sized block of compressed data, writing as much available decompressed data as possible to the output buffer. + // This method is implemented as a coroutine so it may be called as many times as needed. However, for best perf. try not to call it with tiny buffers. + // pState - Pointer to internal decompression state, originally created by lzham_decompress_init. + // pIn_buf, pIn_buf_size - Pointer to input data buffer, and pointer to a size_t containing the number of bytes available in this buffer. + // On return, *pIn_buf_size will be set to the number of bytes read from the buffer. + // pOut_buf, pOut_buf_size - Pointer to the output data buffer, and a pointer to a size_t containing the max number of bytes that can be written to this buffer. + // On return, *pOut_buf_size will be set to the number of bytes written to this buffer. + // no_more_input_bytes_flag - Set to true to indicate that no more input bytes are available to compress (EOF). Once you call this function with this param set to true, it must stay set to true in all future calls. + // Notes: + // In unbuffered mode, the output buffer MUST be large enough to hold the entire decompressed stream. Otherwise, you'll receive the + // LZHAM_DECOMP_STATUS_FAILED_DEST_BUF_TOO_SMALL error (which is currently unrecoverable during unbuffered decompression). + // In buffered mode, if the output buffer's size is 0 bytes, the caller is indicating that no more output bytes are expected from the + // decompressor. In this case, if the decompressor actually has more bytes you'll receive the LZHAM_DECOMP_STATUS_HAS_MORE_OUTPUT + // error (which is recoverable in the buffered case - just call lzham_decompress() again with a non-zero size output buffer). + LZHAM_DLL_EXPORT lzham_decompress_status_t LZHAM_CDECL lzham_decompress( + lzham_decompress_state_ptr pState, + const lzham_uint8 *pIn_buf, size_t *pIn_buf_size, + lzham_uint8 *pOut_buf, size_t *pOut_buf_size, + lzham_bool no_more_input_bytes_flag); + + // Single function call interface. + LZHAM_DLL_EXPORT lzham_decompress_status_t LZHAM_CDECL lzham_decompress_memory( + const lzham_decompress_params *pParams, + lzham_uint8* pDst_buf, + size_t *pDst_len, + const lzham_uint8* pSrc_buf, + size_t src_len, + lzham_uint32 *pAdler32, + lzham_uint32 *pCrc32); + + // ------------------- zlib-style API Definitions. + + // Important note: LZHAM doesn't internally support the Deflate algorithm, but for API compatibility the "Deflate" and "Inflate" names are retained here. + + typedef unsigned long lzham_z_ulong; + + // Heap allocation callbacks. + // Note that lzham_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. + typedef void *(*lzham_z_alloc_func)(void *opaque, size_t items, size_t size); + typedef void (*lzham_z_free_func)(void *opaque, void *address); + typedef void *(*lzham_z_realloc_func)(void *opaque, void *address, size_t items, size_t size); + + #define LZHAM_Z_ADLER32_INIT (1) + // lzham_adler32() returns the initial adler-32 value to use when called with ptr==NULL. + LZHAM_DLL_EXPORT lzham_z_ulong lzham_z_adler32(lzham_z_ulong adler, const unsigned char *ptr, size_t buf_len); + + #define LZHAM_Z_CRC32_INIT (0) + // lzham_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. + LZHAM_DLL_EXPORT lzham_z_ulong lzham_z_crc32(lzham_z_ulong crc, const unsigned char *ptr, size_t buf_len); + + // Compression strategies. + enum + { + LZHAM_Z_DEFAULT_STRATEGY = 0, + LZHAM_Z_FILTERED = 1, + LZHAM_Z_HUFFMAN_ONLY = 2, + LZHAM_Z_RLE = 3, + LZHAM_Z_FIXED = 4 + }; + + // Method + #define LZHAM_Z_DEFLATED 8 + #define LZHAM_Z_LZHAM 14 + + #define LZHAM_Z_VERSION "10.8.1" + #define LZHAM_Z_VERNUM 0xA810 + #define LZHAM_Z_VER_MAJOR 10 + #define LZHAM_Z_VER_MINOR 8 + #define LZHAM_Z_VER_REVISION 1 + #define LZHAM_Z_VER_SUBREVISION 0 + + // Flush values. + // For compression, you typically only need to use LZHAM_NO_FLUSH and LZHAM_FINISH. + // LZHAM_Z_SYNC_FLUSH and LZHAM_Z_FULL_FLUSH during compression forces compression of all buffered input. + // + // For decompression, you typically only need to use LZHAM_Z_SYNC_FLUSH or LZHAM_Z_FINISH. + // LZHAM_Z_FINISH during decompression guarantees that the output buffer is large enough to hold all remaining data to decompress. + // See http://www.bolet.org/~pornin/deflate-flush.html + // Must directly map to lzham_flush_t + enum + { + LZHAM_Z_NO_FLUSH = 0, // compression/decompression + LZHAM_Z_PARTIAL_FLUSH = 1, // compression/decompression, same as LZHAM_Z_SYNC_FLUSH + LZHAM_Z_SYNC_FLUSH = 2, // compression/decompression, when compressing: flush current block (if any), always outputs sync block (aligns output to byte boundary, a 0xFFFF0000 marker will appear in the output stream) + LZHAM_Z_FULL_FLUSH = 3, // compression/decompression, when compressing: same as LZHAM_Z_SYNC_FLUSH but also forces a full state flush (LZ dictionary, all symbol statistics) + LZHAM_Z_FINISH = 4, // compression/decompression + LZHAM_Z_BLOCK = 5, // not supported + LZHAM_Z_TABLE_FLUSH = 10 // compression only, resets all symbol table update rates to maximum frequency (LZHAM extension) + }; + + // Return status codes. LZHAM_Z_PARAM_ERROR is non-standard. + enum + { + LZHAM_Z_OK = 0, + LZHAM_Z_STREAM_END = 1, + LZHAM_Z_NEED_DICT = 2, + LZHAM_Z_ERRNO = -1, + LZHAM_Z_STREAM_ERROR = -2, + LZHAM_Z_DATA_ERROR = -3, + LZHAM_Z_MEM_ERROR = -4, + LZHAM_Z_BUF_ERROR = -5, + LZHAM_Z_VERSION_ERROR = -6, + LZHAM_Z_PARAM_ERROR = -10000 + }; + + // Compression levels. + enum + { + LZHAM_Z_NO_COMPRESSION = 0, + LZHAM_Z_BEST_SPEED = 1, + LZHAM_Z_BEST_COMPRESSION = 9, + LZHAM_Z_UBER_COMPRESSION = 10, // uber = best with extreme parsing (can be very slow) + LZHAM_Z_DEFAULT_COMPRESSION = -1 + }; + + // Window bits + // Important note: The zlib-style API's default to 32KB dictionary for API compatibility. For improved compression, be sure to call deflateInit2/inflateInit2 and specify larger custom window_bits values! + // If changing the calling code isn't practical, unremark LZHAM_Z_API_FORCE_WINDOW_BITS. + #define LZHAM_Z_DEFAULT_WINDOW_BITS 15 + + // Define LZHAM_Z_API_FORCE_WINDOW_BITS to force the entire library to use a constant value for window_bits (helps with porting) in all zlib API's. + // TODO: Might be useful to provide an API to control this at runtime. + //#define LZHAM_Z_API_FORCE_WINDOW_BITS 23 + + // Data types + #define LZHAM_Z_BINARY 0 + #define LZHAM_Z_TEXT 1 + #define LZHAM_Z_ASCII 1 + #define LZHAM_Z_UNKNOWN 2 + + struct lzham_z_internal_state; + + // Compression/decompression stream struct. + typedef struct + { + const unsigned char *next_in; // pointer to next byte to read + unsigned int avail_in; // number of bytes available at next_in + lzham_z_ulong total_in; // total number of bytes consumed so far + + unsigned char *next_out; // pointer to next byte to write + unsigned int avail_out; // number of bytes that can be written to next_out + lzham_z_ulong total_out; // total number of bytes produced so far + + char *msg; // error msg (unused) + struct lzham_z_internal_state *state; // internal state, allocated by zalloc/zfree + + // LZHAM does not support per-stream heap callbacks. Use lzham_set_memory_callbacks() instead. + // These members are ignored - they are here for backwards compatibility with zlib. + lzham_z_alloc_func zalloc; // optional heap allocation function (defaults to malloc) + lzham_z_free_func zfree; // optional heap free function (defaults to free) + void *opaque; // heap alloc function user pointer + + int data_type; // data_type (unused) + lzham_z_ulong adler32; // adler32 of the source or uncompressed data + lzham_z_ulong crc32; // crc32 of the source or uncompressed data + lzham_z_ulong reserved; // not used + } lzham_z_stream; + + typedef lzham_z_stream *lzham_z_streamp; + + LZHAM_DLL_EXPORT const char *lzham_z_version(void); + + // lzham_deflateInit() initializes a compressor with default options: + // Parameters: + // pStream must point to an initialized lzham_stream struct. + // level must be between [LZHAM_NO_COMPRESSION, LZHAM_BEST_COMPRESSION]. + // level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. + // Return values: + // LZHAM_OK on success. + // LZHAM_STREAM_ERROR if the stream is bogus. + // LZHAM_PARAM_ERROR if the input parameters are bogus. + // LZHAM_MEM_ERROR on out of memory. + LZHAM_DLL_EXPORT int lzham_z_deflateInit(lzham_z_streamp pStream, int level); + + // lzham_deflateInit2() is like lzham_deflate(), except with more control: + // Additional parameters: + // method must be LZHAM_Z_DEFLATED or LZHAM_Z_LZHAM (LZHAM_Z_DEFLATED will be internally converted to LZHAM_Z_LZHAM with a windowsize of LZHAM_Z_DEFAULT_WINDOW_BITS) + // window_bits must be LZHAM_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -LZHAM_Z_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) + // mem_level must be between [1, 9] (it's checked but ignored by lzham) + LZHAM_DLL_EXPORT int lzham_z_deflateInit2(lzham_z_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + + // Quickly resets a compressor without having to reallocate anything. Same as calling lzham_z_deflateEnd() followed by lzham_z_deflateInit()/lzham_z_deflateInit2(). + LZHAM_DLL_EXPORT int lzham_z_deflateReset(lzham_z_streamp pStream); + + // lzham_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. + // Parameters: + // pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. + // flush may be LZHAM_Z_NO_FLUSH, LZHAM_Z_PARTIAL_FLUSH/LZHAM_Z_SYNC_FLUSH, LZHAM_Z_FULL_FLUSH, or LZHAM_Z_FINISH. + // Return values: + // LZHAM_Z_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). + // LZHAM_Z_STREAM_END if all input has been consumed and all output bytes have been written. Don't call lzham_z_deflate() on the stream anymore. + // LZHAM_Z_STREAM_ERROR if the stream is bogus. + // LZHAM_Z_PARAM_ERROR if one of the parameters is invalid. + // LZHAM_Z_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) + LZHAM_DLL_EXPORT int lzham_z_deflate(lzham_z_streamp pStream, int flush); + + // lzham_deflateEnd() deinitializes a compressor: + // Return values: + // LZHAM_Z_OK on success. + // LZHAM_Z_STREAM_ERROR if the stream is bogus. + LZHAM_DLL_EXPORT int lzham_z_deflateEnd(lzham_z_streamp pStream); + + // lzham_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by lzham_z_deflate(), assuming flush is set to only LZHAM_Z_NO_FLUSH or LZHAM_Z_FINISH. + LZHAM_DLL_EXPORT lzham_z_ulong lzham_z_deflateBound(lzham_z_streamp pStream, lzham_z_ulong source_len); + + // Single-call compression functions lzham_z_compress() and lzham_z_compress2(): + // Returns LZHAM_Z_OK on success, or one of the error codes from lzham_z_deflate() on failure. + LZHAM_DLL_EXPORT int lzham_z_compress(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len); + LZHAM_DLL_EXPORT int lzham_z_compress2(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len, int level); + + // lzham_z_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling lzham_z_compress(). + LZHAM_DLL_EXPORT lzham_z_ulong lzham_z_compressBound(lzham_z_ulong source_len); + + // Initializes a decompressor. + LZHAM_DLL_EXPORT int lzham_z_inflateInit(lzham_z_streamp pStream); + + // lzham_z_inflateInit2() is like lzham_z_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: + // window_bits must be LZHAM_Z_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -LZHAM_Z_DEFAULT_WINDOW_BITS (raw stream with no zlib header/footer). + LZHAM_DLL_EXPORT int lzham_z_inflateInit2(lzham_z_streamp pStream, int window_bits); + + LZHAM_DLL_EXPORT int lzham_z_inflateReset(lzham_z_streamp pStream); + + // Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. + // Parameters: + // pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. + // flush may be LZHAM_Z_NO_FLUSH, LZHAM_Z_SYNC_FLUSH, or LZHAM_Z_FINISH. + // On the first call, if flush is LZHAM_Z_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). + // LZHAM_Z_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. + // Return values: + // LZHAM_Z_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. + // LZHAM_Z_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. + // LZHAM_Z_STREAM_ERROR if the stream is bogus. + // LZHAM_Z_DATA_ERROR if the deflate stream is invalid. + // LZHAM_Z_PARAM_ERROR if one of the parameters is invalid. + // LZHAM_Z_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call lzham_inflate() again + // with more input data, or with more room in the output buffer (except when using single call decompression, described above). + LZHAM_DLL_EXPORT int lzham_z_inflate(lzham_z_streamp pStream, int flush); + + // Deinitializes a decompressor. + LZHAM_DLL_EXPORT int lzham_z_inflateEnd(lzham_z_streamp pStream); + + // Single-call decompression. + // Returns LZHAM_OK on success, or one of the error codes from lzham_inflate() on failure. + LZHAM_DLL_EXPORT int lzham_z_uncompress(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len); + + // Returns a string description of the specified error code, or NULL if the error code is invalid. + LZHAM_DLL_EXPORT const char *lzham_z_error(int err); + + // Redefine zlib-compatible names to lzham equivalents, so lzham can be used as a more or less drop-in replacement for the subset of zlib that lzham supports. + // Define LZHAM_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. + #ifdef LZHAM_DEFINE_ZLIB_API + typedef unsigned char Byte; + typedef unsigned int uInt; + typedef lzham_z_ulong uLong; + typedef Byte Bytef; + typedef uInt uIntf; + typedef char charf; + typedef int intf; + typedef void *voidpf; + typedef uLong uLongf; + typedef void *voidp; + typedef void *const voidpc; + #define Z_NULL 0 + #define Z_NO_FLUSH LZHAM_Z_NO_FLUSH + #define Z_PARTIAL_FLUSH LZHAM_Z_PARTIAL_FLUSH + #define Z_SYNC_FLUSH LZHAM_Z_SYNC_FLUSH + #define Z_FULL_FLUSH LZHAM_Z_FULL_FLUSH + #define Z_FINISH LZHAM_Z_FINISH + #define Z_BLOCK LZHAM_Z_BLOCK + #define Z_OK LZHAM_Z_OK + #define Z_STREAM_END LZHAM_Z_STREAM_END + #define Z_NEED_DICT LZHAM_Z_NEED_DICT + #define Z_ERRNO LZHAM_Z_ERRNO + #define Z_STREAM_ERROR LZHAM_Z_STREAM_ERROR + #define Z_DATA_ERROR LZHAM_Z_DATA_ERROR + #define Z_MEM_ERROR LZHAM_Z_MEM_ERROR + #define Z_BUF_ERROR LZHAM_Z_BUF_ERROR + #define Z_VERSION_ERROR LZHAM_Z_VERSION_ERROR + #define Z_PARAM_ERROR LZHAM_Z_PARAM_ERROR + #define Z_NO_COMPRESSION LZHAM_Z_NO_COMPRESSION + #define Z_BEST_SPEED LZHAM_Z_BEST_SPEED + #define Z_BEST_COMPRESSION LZHAM_Z_BEST_COMPRESSION + #define Z_DEFAULT_COMPRESSION LZHAM_Z_DEFAULT_COMPRESSION + #define Z_DEFAULT_STRATEGY LZHAM_Z_DEFAULT_STRATEGY + #define Z_FILTERED LZHAM_Z_FILTERED + #define Z_HUFFMAN_ONLY LZHAM_Z_HUFFMAN_ONLY + #define Z_RLE LZHAM_Z_RLE + #define Z_FIXED LZHAM_Z_FIXED + #define Z_DEFLATED LZHAM_Z_DEFLATED + #define Z_DEFAULT_WINDOW_BITS LZHAM_Z_DEFAULT_WINDOW_BITS + #define alloc_func lzham_z_alloc_func + #define free_func lzham_z_free_func + #define internal_state lzham_z_internal_state + #define z_stream lzham_z_stream + #define deflateInit lzham_z_deflateInit + #define deflateInit2 lzham_z_deflateInit2 + #define deflateReset lzham_z_deflateReset + #define deflate lzham_z_deflate + #define deflateEnd lzham_z_deflateEnd + #define deflateBound lzham_z_deflateBound + #define compress lzham_z_compress + #define compress2 lzham_z_compress2 + #define compressBound lzham_z_compressBound + #define inflateInit lzham_z_inflateInit + #define inflateInit2 lzham_z_inflateInit2 + #define inflateReset lzham_z_inflateReset + #define inflate lzham_z_inflate + #define inflateEnd lzham_z_inflateEnd + #define uncompress lzham_z_uncompress + #define crc32 lzham_z_crc32 + #define adler32 lzham_z_adler32 + #define MAX_WBITS 26 + #define MAX_MEM_LEVEL 9 + #define zError lzham_z_error + #define ZLIB_VERSION LZHAM_Z_VERSION + #define ZLIB_VERNUM LZHAM_Z_VERNUM + #define ZLIB_VER_MAJOR LZHAM_Z_VER_MAJOR + #define ZLIB_VER_MINOR LZHAM_Z_VER_MINOR + #define ZLIB_VER_REVISION LZHAM_Z_VER_REVISION + #define ZLIB_VER_SUBREVISION LZHAM_Z_VER_SUBREVISION + #define zlibVersion lzham_z_version + #define zlib_version lzham_z_version() + #define Z_BINARY LZHAM_Z_BINARY + #define Z_TEXT LZHAM_Z_TEST + #define Z_ASCII LZHAM_Z_ASCII + #define Z_UNKNOWN LZHAM_Z_UNKNOWN + #endif // #ifdef LZHAM_DEFINE_ZLIB_API + + // Exported function typedefs, to simplify loading the LZHAM DLL dynamically. + typedef lzham_uint32 (LZHAM_CDECL *lzham_get_version_func)(void); + typedef void (LZHAM_CDECL *lzham_set_memory_callbacks_func)(lzham_realloc_func pRealloc, lzham_msize_func pMSize, void* pUser_data); + + typedef lzham_compress_state_ptr (LZHAM_CDECL *lzham_compress_init_func)(const lzham_compress_params *pParams); + typedef lzham_compress_state_ptr (LZHAM_CDECL *lzham_compress_reinit_func)(lzham_compress_state_ptr pState); + typedef lzham_compress_checksums* (LZHAM_CDECL *lzham_compress_deinit_func)(lzham_compress_state_ptr pState); + typedef lzham_compress_status_t (LZHAM_CDECL *lzham_compress_func)(lzham_compress_state_ptr pState, const lzham_uint8 *pIn_buf, size_t *pIn_buf_size, lzham_uint8 *pOut_buf, size_t *pOut_buf_size, lzham_bool no_more_input_bytes_flag); + typedef lzham_compress_status_t (LZHAM_CDECL *lzham_compress2_func)(lzham_compress_state_ptr pState, const lzham_uint8 *pIn_buf, size_t *pIn_buf_size, lzham_uint8 *pOut_buf, size_t *pOut_buf_size, lzham_flush_t flush_type); + typedef lzham_compress_status_t (LZHAM_CDECL *lzham_compress_memory_func)(const lzham_compress_params *pParams, lzham_uint8* pDst_buf, size_t *pDst_len, const lzham_uint8* pSrc_buf, size_t src_len, lzham_uint32 *pAdler32, lzham_uint32* pCrc32); + + typedef lzham_decompress_state_ptr (LZHAM_CDECL *lzham_decompress_init_func)(const lzham_decompress_params *pParams); + typedef lzham_decompress_state_ptr (LZHAM_CDECL *lzham_decompress_reinit_func)(lzham_compress_state_ptr pState, const lzham_decompress_params *pParams); + typedef lzham_decompress_checksums* (LZHAM_CDECL *lzham_decompress_deinit_func)(lzham_decompress_state_ptr pState); + typedef lzham_decompress_status_t (LZHAM_CDECL *lzham_decompress_func)(lzham_decompress_state_ptr pState, const lzham_uint8 *pIn_buf, size_t *pIn_buf_size, lzham_uint8 *pOut_buf, size_t *pOut_buf_size, lzham_bool no_more_input_bytes_flag); + typedef lzham_decompress_status_t (LZHAM_CDECL *lzham_decompress_memory_func)(const lzham_decompress_params *pParams, lzham_uint8* pDst_buf, size_t *pDst_len, const lzham_uint8* pSrc_buf, size_t src_len, lzham_uint32 *pAdler32, lzham_uint32 *pCrc32); + + typedef const char *(LZHAM_CDECL *lzham_z_version_func)(void); + typedef int (LZHAM_CDECL *lzham_z_deflateInit_func)(lzham_z_streamp pStream, int level); + typedef int (LZHAM_CDECL *lzham_z_deflateInit2_func)(lzham_z_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + typedef int (LZHAM_CDECL *lzham_z_deflateReset_func)(lzham_z_streamp pStream); + typedef int (LZHAM_CDECL *lzham_z_deflate_func)(lzham_z_streamp pStream, int flush); + typedef int (LZHAM_CDECL *lzham_z_deflateEnd_func)(lzham_z_streamp pStream); + typedef lzham_z_ulong (LZHAM_CDECL *lzham_z_deflateBound_func)(lzham_z_streamp pStream, lzham_z_ulong source_len); + typedef int (LZHAM_CDECL *lzham_z_compress_func)(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len); + typedef int (LZHAM_CDECL *lzham_z_compress2_func)(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len, int level); + typedef lzham_z_ulong (LZHAM_CDECL *lzham_z_compressBound_func)(lzham_z_ulong source_len); + typedef int (LZHAM_CDECL *lzham_z_inflateInit_func)(lzham_z_streamp pStream); + typedef int (LZHAM_CDECL *lzham_z_inflateInit2_func)(lzham_z_streamp pStream, int window_bits); + typedef int (LZHAM_CDECL *lzham_z_inflateReset_func)(lzham_z_streamp pStream); + typedef int (LZHAM_CDECL *lzham_z_inflate_func)(lzham_z_streamp pStream, int flush); + typedef int (LZHAM_CDECL *lzham_z_inflateEnd_func)(lzham_z_streamp pStream); + typedef int (LZHAM_CDECL *lzham_z_uncompress_func)(unsigned char *pDest, lzham_z_ulong *pDest_len, const unsigned char *pSource, lzham_z_ulong source_len); + typedef const char *(LZHAM_CDECL *lzham_z_error_func)(int err); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +// This optional interface is used by the dynamic/static link helpers defined in lzham_dynamic_lib.h and lzham_static_lib.h. +// It allows code to always call LZHAM the same way, independent of how it was linked into the app (statically or dynamically). +class ilzham +{ + ilzham(const ilzham &other); + ilzham& operator= (const ilzham &rhs); + +public: + ilzham() { clear(); } + + virtual ~ilzham() { } + virtual bool load() = 0; + virtual void unload() = 0; + virtual bool is_loaded() = 0; + + void clear() + { + this->lzham_get_version = NULL; + this->lzham_set_memory_callbacks = NULL; + + this->lzham_compress_init = NULL; + this->lzham_compress_reinit = NULL; + this->lzham_compress_deinit = NULL; + this->lzham_compress = NULL; + this->lzham_compress2 = NULL; + this->lzham_compress_memory = NULL; + + this->lzham_decompress_init = NULL; + this->lzham_decompress_reinit = NULL; + this->lzham_decompress_deinit = NULL; + this->lzham_decompress = NULL; + this->lzham_decompress_memory = NULL; + + this->lzham_z_version = NULL; + this->lzham_z_deflateInit = NULL; + this->lzham_z_deflateInit2 = NULL; + this->lzham_z_deflateReset = NULL; + this->lzham_z_deflate = NULL; + this->lzham_z_deflateEnd = NULL; + this->lzham_z_deflateBound = NULL; + this->lzham_z_compress = NULL; + this->lzham_z_compress2 = NULL; + this->lzham_z_compressBound = NULL; + this->lzham_z_inflateInit = NULL; + this->lzham_z_inflateInit2 = NULL; + this->lzham_z_inflate = NULL; + this->lzham_z_inflateEnd = NULL; + this->lzham_z_uncompress = NULL; + this->lzham_z_error = NULL; + } + + lzham_get_version_func lzham_get_version; + lzham_set_memory_callbacks_func lzham_set_memory_callbacks; + + lzham_compress_init_func lzham_compress_init; + lzham_compress_reinit_func lzham_compress_reinit; + lzham_compress_deinit_func lzham_compress_deinit; + lzham_compress_func lzham_compress; + lzham_compress2_func lzham_compress2; + lzham_compress_memory_func lzham_compress_memory; + + lzham_decompress_init_func lzham_decompress_init; + lzham_decompress_reinit_func lzham_decompress_reinit; + lzham_decompress_deinit_func lzham_decompress_deinit; + lzham_decompress_func lzham_decompress; + lzham_decompress_memory_func lzham_decompress_memory; + + lzham_z_version_func lzham_z_version; + lzham_z_deflateInit_func lzham_z_deflateInit; + lzham_z_deflateInit2_func lzham_z_deflateInit2; + lzham_z_deflateReset_func lzham_z_deflateReset; + lzham_z_deflate_func lzham_z_deflate; + lzham_z_deflateEnd_func lzham_z_deflateEnd; + lzham_z_deflateBound_func lzham_z_deflateBound; + lzham_z_compress_func lzham_z_compress; + lzham_z_compress2_func lzham_z_compress2; + lzham_z_compressBound_func lzham_z_compressBound; + lzham_z_inflateInit_func lzham_z_inflateInit; + lzham_z_inflateInit2_func lzham_z_inflateInit2; + lzham_z_inflateReset_func lzham_z_inflateReset; + lzham_z_inflate_func lzham_z_inflate; + lzham_z_inflateEnd_func lzham_z_inflateEnd; + lzham_z_uncompress_func lzham_z_uncompress; + lzham_z_error_func lzham_z_error; +}; +#endif // #ifdef __cplusplus + +#endif // #ifndef __LZHAM_H__ + +// Copyright (c) 2009-2012 Richard Geldreich, Jr. +// +// LZHAM uses the MIT License: +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + diff --git a/r5dev/thirdparty/lzham/include/lzham_static_lib.h b/r5dev/thirdparty/lzham/include/lzham_static_lib.h new file mode 100644 index 00000000..274ccb12 --- /dev/null +++ b/r5dev/thirdparty/lzham/include/lzham_static_lib.h @@ -0,0 +1,57 @@ +#pragma once + +#define LZHAM_STATIC_LIB 1 +#include "lzham.h" + +#ifdef __cplusplus +// Like lzham_dynamic_lib, except it sets the function pointer members to point directly to the C functions in lzhamlib +class lzham_static_lib : public ilzham +{ + lzham_static_lib(const lzham_static_lib &other); + lzham_static_lib& operator= (const lzham_static_lib &rhs); + +public: + lzham_static_lib() : ilzham() { } + + virtual ~lzham_static_lib() { } + + virtual bool load() + { + this->lzham_get_version = ::lzham_get_version; + this->lzham_set_memory_callbacks = ::lzham_set_memory_callbacks; + this->lzham_compress_init = ::lzham_compress_init; + this->lzham_compress_deinit = ::lzham_compress_deinit; + this->lzham_compress = ::lzham_compress; + this->lzham_compress2 = ::lzham_compress2; + this->lzham_compress_memory = ::lzham_compress_memory; + this->lzham_decompress_init = ::lzham_decompress_init; + this->lzham_decompress_reinit = ::lzham_decompress_reinit; + this->lzham_decompress_deinit = ::lzham_decompress_deinit; + this->lzham_decompress = ::lzham_decompress; + this->lzham_decompress_memory = ::lzham_decompress_memory; + + this->lzham_z_version = ::lzham_z_version; + this->lzham_z_deflateInit = ::lzham_z_deflateInit; + this->lzham_z_deflateInit2 = ::lzham_z_deflateInit2; + this->lzham_z_deflateReset = ::lzham_z_deflateReset; + this->lzham_z_deflate = ::lzham_z_deflate; + this->lzham_z_deflateEnd = ::lzham_z_deflateEnd; + this->lzham_z_deflateBound = ::lzham_z_deflateBound; + this->lzham_z_compress = ::lzham_z_compress; + this->lzham_z_compress2 = ::lzham_z_compress2; + this->lzham_z_compressBound = ::lzham_z_compressBound; + this->lzham_z_inflateInit = ::lzham_z_inflateInit; + this->lzham_z_inflateInit2 = ::lzham_z_inflateInit2; + this->lzham_z_inflate = ::lzham_z_inflate; + this->lzham_z_inflateEnd = ::lzham_z_inflateEnd; + this->lzham_z_uncompress = ::lzham_z_uncompress; + this->lzham_z_error = ::lzham_z_error; + + return true; + } + + virtual void unload() { clear(); } + + virtual bool is_loaded() { return lzham_get_version != NULL; } +}; +#endif // __cplusplus diff --git a/r5dev/thirdparty/lzham/libs/lzhamcomp_x64.lib b/r5dev/thirdparty/lzham/libs/lzhamcomp_x64.lib new file mode 100644 index 00000000..7f5d6e43 Binary files /dev/null and b/r5dev/thirdparty/lzham/libs/lzhamcomp_x64.lib differ diff --git a/r5dev/thirdparty/lzham/libs/lzhamcomp_x64D.lib b/r5dev/thirdparty/lzham/libs/lzhamcomp_x64D.lib new file mode 100644 index 00000000..f593696f Binary files /dev/null and b/r5dev/thirdparty/lzham/libs/lzhamcomp_x64D.lib differ diff --git a/r5dev/thirdparty/lzham/libs/lzhamdecomp_x64.lib b/r5dev/thirdparty/lzham/libs/lzhamdecomp_x64.lib new file mode 100644 index 00000000..5772956d Binary files /dev/null and b/r5dev/thirdparty/lzham/libs/lzhamdecomp_x64.lib differ diff --git a/r5dev/thirdparty/lzham/libs/lzhamdecomp_x64D.lib b/r5dev/thirdparty/lzham/libs/lzhamdecomp_x64D.lib new file mode 100644 index 00000000..a6665324 Binary files /dev/null and b/r5dev/thirdparty/lzham/libs/lzhamdecomp_x64D.lib differ diff --git a/r5dev/thirdparty/lzham/libs/lzhamlib_x64.lib b/r5dev/thirdparty/lzham/libs/lzhamlib_x64.lib new file mode 100644 index 00000000..18ac9a96 Binary files /dev/null and b/r5dev/thirdparty/lzham/libs/lzhamlib_x64.lib differ diff --git a/r5dev/thirdparty/lzham/libs/lzhamlib_x64D.lib b/r5dev/thirdparty/lzham/libs/lzhamlib_x64D.lib new file mode 100644 index 00000000..121dc906 Binary files /dev/null and b/r5dev/thirdparty/lzham/libs/lzhamlib_x64D.lib differ diff --git a/external/spdlog/include/async.h b/r5dev/thirdparty/spdlog/include/async.h similarity index 95% rename from external/spdlog/include/async.h rename to r5dev/thirdparty/spdlog/include/async.h index 98e8c25a..65e0f3ba 100644 --- a/external/spdlog/include/async.h +++ b/r5dev/thirdparty/spdlog/include/async.h @@ -14,9 +14,9 @@ // This is because each message in the queue holds a shared_ptr to the // originating logger. -#include -#include
-#include
+#include +#include +#include #include #include diff --git a/external/spdlog/include/async_logger-inl.h b/r5dev/thirdparty/spdlog/include/async_logger-inl.h similarity index 93% rename from external/spdlog/include/async_logger-inl.h rename to r5dev/thirdparty/spdlog/include/async_logger-inl.h index 81ffb6f2..2c95584b 100644 --- a/external/spdlog/include/async_logger-inl.h +++ b/r5dev/thirdparty/spdlog/include/async_logger-inl.h @@ -4,11 +4,11 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include -#include
+#include +#include #include #include diff --git a/external/spdlog/include/async_logger.h b/r5dev/thirdparty/spdlog/include/async_logger.h similarity index 97% rename from external/spdlog/include/async_logger.h rename to r5dev/thirdparty/spdlog/include/async_logger.h index a67b1562..b43f1aff 100644 --- a/external/spdlog/include/async_logger.h +++ b/r5dev/thirdparty/spdlog/include/async_logger.h @@ -14,7 +14,7 @@ // Upon destruction, logs all remaining messages in the queue before // destructing.. -#include +#include namespace spdlog { diff --git a/external/spdlog/include/cfg/argv.h b/r5dev/thirdparty/spdlog/include/cfg/argv.h similarity index 91% rename from external/spdlog/include/cfg/argv.h rename to r5dev/thirdparty/spdlog/include/cfg/argv.h index 81e69564..c6a93fc8 100644 --- a/external/spdlog/include/cfg/argv.h +++ b/r5dev/thirdparty/spdlog/include/cfg/argv.h @@ -2,8 +2,8 @@ // Distributed under the MIT License (http://opensource.org/licenses/MIT) #pragma once -#include -#include
+#include +#include // // Init log levels using each argv entry that starts with "SPDLOG_LEVEL=" diff --git a/external/spdlog/include/cfg/env.h b/r5dev/thirdparty/spdlog/include/cfg/env.h similarity index 84% rename from external/spdlog/include/cfg/env.h rename to r5dev/thirdparty/spdlog/include/cfg/env.h index 0faf4f96..dcf1b8e0 100644 --- a/external/spdlog/include/cfg/env.h +++ b/r5dev/thirdparty/spdlog/include/cfg/env.h @@ -2,9 +2,9 @@ // Distributed under the MIT License (http://opensource.org/licenses/MIT) #pragma once -#include -#include
-#include
+#include +#include +#include // // Init levels and patterns from env variables SPDLOG_LEVEL diff --git a/external/spdlog/include/cfg/helpers-inl.h b/r5dev/thirdparty/spdlog/include/cfg/helpers-inl.h similarity index 93% rename from external/spdlog/include/cfg/helpers-inl.h rename to r5dev/thirdparty/spdlog/include/cfg/helpers-inl.h index 8e480650..254bd0ae 100644 --- a/external/spdlog/include/cfg/helpers-inl.h +++ b/r5dev/thirdparty/spdlog/include/cfg/helpers-inl.h @@ -4,12 +4,12 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include -#include
-#include
+#include +#include +#include #include #include diff --git a/external/spdlog/include/cfg/helpers.h b/r5dev/thirdparty/spdlog/include/cfg/helpers.h similarity index 93% rename from external/spdlog/include/cfg/helpers.h rename to r5dev/thirdparty/spdlog/include/cfg/helpers.h index be44817a..96f4625f 100644 --- a/external/spdlog/include/cfg/helpers.h +++ b/r5dev/thirdparty/spdlog/include/cfg/helpers.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include namespace spdlog { diff --git a/external/spdlog/include/common-inl.h b/r5dev/thirdparty/spdlog/include/common-inl.h similarity index 97% rename from external/spdlog/include/common-inl.h rename to r5dev/thirdparty/spdlog/include/common-inl.h index 0c629451..fd498447 100644 --- a/external/spdlog/include/common-inl.h +++ b/r5dev/thirdparty/spdlog/include/common-inl.h @@ -4,7 +4,7 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif #include diff --git a/external/spdlog/include/common.h b/r5dev/thirdparty/spdlog/include/common.h similarity index 98% rename from external/spdlog/include/common.h rename to r5dev/thirdparty/spdlog/include/common.h index 9c50d1d5..66e05cf9 100644 --- a/external/spdlog/include/common.h +++ b/r5dev/thirdparty/spdlog/include/common.h @@ -3,8 +3,8 @@ #pragma once -#include -#include
+#include +#include #include #include @@ -33,7 +33,7 @@ # define SPDLOG_INLINE inline #endif // #ifdef SPDLOG_COMPILED_LIB -#include +#include #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) # include diff --git a/external/spdlog/include/details/backtracer-inl.h b/r5dev/thirdparty/spdlog/include/details/backtracer-inl.h similarity index 96% rename from external/spdlog/include/details/backtracer-inl.h rename to r5dev/thirdparty/spdlog/include/details/backtracer-inl.h index a184091b..7e838779 100644 --- a/external/spdlog/include/details/backtracer-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/backtracer-inl.h @@ -4,7 +4,7 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
+#include #endif namespace spdlog { namespace details { diff --git a/external/spdlog/include/details/backtracer.h b/r5dev/thirdparty/spdlog/include/details/backtracer.h similarity index 89% rename from external/spdlog/include/details/backtracer.h rename to r5dev/thirdparty/spdlog/include/details/backtracer.h index 986b496c..ab449917 100644 --- a/external/spdlog/include/details/backtracer.h +++ b/r5dev/thirdparty/spdlog/include/details/backtracer.h @@ -3,8 +3,8 @@ #pragma once -#include
-#include
+#include +#include #include #include diff --git a/external/spdlog/include/details/circular_q.h b/r5dev/thirdparty/spdlog/include/details/circular_q.h similarity index 100% rename from external/spdlog/include/details/circular_q.h rename to r5dev/thirdparty/spdlog/include/details/circular_q.h diff --git a/external/spdlog/include/details/console_globals.h b/r5dev/thirdparty/spdlog/include/details/console_globals.h similarity index 90% rename from external/spdlog/include/details/console_globals.h rename to r5dev/thirdparty/spdlog/include/details/console_globals.h index e8cc76ed..a801112a 100644 --- a/external/spdlog/include/details/console_globals.h +++ b/r5dev/thirdparty/spdlog/include/details/console_globals.h @@ -3,7 +3,7 @@ #pragma once -#include
+#include #include namespace spdlog { diff --git a/external/spdlog/include/details/file_helper-inl.h b/r5dev/thirdparty/spdlog/include/details/file_helper-inl.h similarity index 95% rename from external/spdlog/include/details/file_helper-inl.h rename to r5dev/thirdparty/spdlog/include/details/file_helper-inl.h index e6bd6966..2aff8925 100644 --- a/external/spdlog/include/details/file_helper-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/file_helper-inl.h @@ -4,11 +4,11 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
+#include #endif -#include
-#include +#include +#include #include #include diff --git a/external/spdlog/include/details/file_helper.h b/r5dev/thirdparty/spdlog/include/details/file_helper.h similarity index 97% rename from external/spdlog/include/details/file_helper.h rename to r5dev/thirdparty/spdlog/include/details/file_helper.h index d3a01cce..8b469977 100644 --- a/external/spdlog/include/details/file_helper.h +++ b/r5dev/thirdparty/spdlog/include/details/file_helper.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include namespace spdlog { diff --git a/external/spdlog/include/details/fmt_helper.h b/r5dev/thirdparty/spdlog/include/details/fmt_helper.h similarity index 97% rename from external/spdlog/include/details/fmt_helper.h rename to r5dev/thirdparty/spdlog/include/details/fmt_helper.h index 53045dbf..0b433247 100644 --- a/external/spdlog/include/details/fmt_helper.h +++ b/r5dev/thirdparty/spdlog/include/details/fmt_helper.h @@ -5,8 +5,8 @@ #include #include #include -#include -#include +#include +#include // Some fmt helpers to efficiently format and pad ints and strings namespace spdlog { diff --git a/external/spdlog/include/details/log_msg-inl.h b/r5dev/thirdparty/spdlog/include/details/log_msg-inl.h similarity index 90% rename from external/spdlog/include/details/log_msg-inl.h rename to r5dev/thirdparty/spdlog/include/details/log_msg-inl.h index 43c07712..fd632d24 100644 --- a/external/spdlog/include/details/log_msg-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/log_msg-inl.h @@ -4,10 +4,10 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
+#include #endif -#include
+#include namespace spdlog { namespace details { diff --git a/external/spdlog/include/details/log_msg.h b/r5dev/thirdparty/spdlog/include/details/log_msg.h similarity index 96% rename from external/spdlog/include/details/log_msg.h rename to r5dev/thirdparty/spdlog/include/details/log_msg.h index 0ec1d668..6b3884aa 100644 --- a/external/spdlog/include/details/log_msg.h +++ b/r5dev/thirdparty/spdlog/include/details/log_msg.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include namespace spdlog { diff --git a/external/spdlog/include/details/log_msg_buffer-inl.h b/r5dev/thirdparty/spdlog/include/details/log_msg_buffer-inl.h similarity index 96% rename from external/spdlog/include/details/log_msg_buffer-inl.h rename to r5dev/thirdparty/spdlog/include/details/log_msg_buffer-inl.h index eaac84d2..c2852764 100644 --- a/external/spdlog/include/details/log_msg_buffer-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/log_msg_buffer-inl.h @@ -4,7 +4,7 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
+#include #endif namespace spdlog { diff --git a/external/spdlog/include/details/log_msg_buffer.h b/r5dev/thirdparty/spdlog/include/details/log_msg_buffer.h similarity index 94% rename from external/spdlog/include/details/log_msg_buffer.h rename to r5dev/thirdparty/spdlog/include/details/log_msg_buffer.h index 1a46a337..a886e5ef 100644 --- a/external/spdlog/include/details/log_msg_buffer.h +++ b/r5dev/thirdparty/spdlog/include/details/log_msg_buffer.h @@ -3,7 +3,7 @@ #pragma once -#include
+#include namespace spdlog { namespace details { diff --git a/external/spdlog/include/details/mpmc_blocking_q.h b/r5dev/thirdparty/spdlog/include/details/mpmc_blocking_q.h similarity index 98% rename from external/spdlog/include/details/mpmc_blocking_q.h rename to r5dev/thirdparty/spdlog/include/details/mpmc_blocking_q.h index 16643320..e5a1cb76 100644 --- a/external/spdlog/include/details/mpmc_blocking_q.h +++ b/r5dev/thirdparty/spdlog/include/details/mpmc_blocking_q.h @@ -10,7 +10,7 @@ // dequeue_for(..) - will block until the queue is not empty or timeout have // passed. -#include
+#include #include #include diff --git a/external/spdlog/include/details/null_mutex.h b/r5dev/thirdparty/spdlog/include/details/null_mutex.h similarity index 100% rename from external/spdlog/include/details/null_mutex.h rename to r5dev/thirdparty/spdlog/include/details/null_mutex.h diff --git a/external/spdlog/include/details/os-inl.h b/r5dev/thirdparty/spdlog/include/details/os-inl.h similarity index 99% rename from external/spdlog/include/details/os-inl.h rename to r5dev/thirdparty/spdlog/include/details/os-inl.h index 4c642711..80023274 100644 --- a/external/spdlog/include/details/os-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/os-inl.h @@ -4,10 +4,10 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
+#include #endif -#include +#include #include #include @@ -25,7 +25,7 @@ #include // _get_osfhandle and _isatty support #include // _get_pid support -#include
+#include #ifdef __MINGW32__ #include diff --git a/external/spdlog/include/details/os.h b/r5dev/thirdparty/spdlog/include/details/os.h similarity index 98% rename from external/spdlog/include/details/os.h rename to r5dev/thirdparty/spdlog/include/details/os.h index 7e32c345..56c307ba 100644 --- a/external/spdlog/include/details/os.h +++ b/r5dev/thirdparty/spdlog/include/details/os.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include // std::time_t namespace spdlog { diff --git a/external/spdlog/include/details/periodic_worker-inl.h b/r5dev/thirdparty/spdlog/include/details/periodic_worker-inl.h similarity index 94% rename from external/spdlog/include/details/periodic_worker-inl.h rename to r5dev/thirdparty/spdlog/include/details/periodic_worker-inl.h index 101d9901..6df6e3db 100644 --- a/external/spdlog/include/details/periodic_worker-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/periodic_worker-inl.h @@ -4,7 +4,7 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
+#include #endif namespace spdlog { diff --git a/external/spdlog/include/details/periodic_worker.h b/r5dev/thirdparty/spdlog/include/details/periodic_worker.h similarity index 100% rename from external/spdlog/include/details/periodic_worker.h rename to r5dev/thirdparty/spdlog/include/details/periodic_worker.h diff --git a/external/spdlog/include/details/registry-inl.h b/r5dev/thirdparty/spdlog/include/details/registry-inl.h similarity index 95% rename from external/spdlog/include/details/registry-inl.h rename to r5dev/thirdparty/spdlog/include/details/registry-inl.h index 0fd34ce7..0f3b63e5 100644 --- a/external/spdlog/include/details/registry-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/registry-inl.h @@ -4,20 +4,20 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
+#include #endif -#include -#include
-#include -#include +#include +#include +#include +#include #ifndef SPDLOG_DISABLE_DEFAULT_LOGGER // support for the default stdout color logger #ifdef _WIN32 -#include +#include #else -#include +#include #endif #endif // SPDLOG_DISABLE_DEFAULT_LOGGER diff --git a/external/spdlog/include/details/registry.h b/r5dev/thirdparty/spdlog/include/details/registry.h similarity index 98% rename from external/spdlog/include/details/registry.h rename to r5dev/thirdparty/spdlog/include/details/registry.h index 00bde16a..7a274c20 100644 --- a/external/spdlog/include/details/registry.h +++ b/r5dev/thirdparty/spdlog/include/details/registry.h @@ -8,7 +8,7 @@ // If user requests a non existing logger, nullptr will be returned // This class is thread safe -#include +#include #include #include diff --git a/external/spdlog/include/details/synchronous_factory.h b/r5dev/thirdparty/spdlog/include/details/synchronous_factory.h similarity index 100% rename from external/spdlog/include/details/synchronous_factory.h rename to r5dev/thirdparty/spdlog/include/details/synchronous_factory.h diff --git a/external/spdlog/include/details/tcp_client-windows.h b/r5dev/thirdparty/spdlog/include/details/tcp_client-windows.h similarity index 97% rename from external/spdlog/include/details/tcp_client-windows.h rename to r5dev/thirdparty/spdlog/include/details/tcp_client-windows.h index d3d372ab..ec237d3b 100644 --- a/external/spdlog/include/details/tcp_client-windows.h +++ b/r5dev/thirdparty/spdlog/include/details/tcp_client-windows.h @@ -5,8 +5,8 @@ #define WIN32_LEAN_AND_MEAN // tcp client helper -#include -#include
+#include +#include #include #include diff --git a/external/spdlog/include/details/tcp_client.h b/r5dev/thirdparty/spdlog/include/details/tcp_client.h similarity index 97% rename from external/spdlog/include/details/tcp_client.h rename to r5dev/thirdparty/spdlog/include/details/tcp_client.h index ba574aea..ff83512e 100644 --- a/external/spdlog/include/details/tcp_client.h +++ b/r5dev/thirdparty/spdlog/include/details/tcp_client.h @@ -8,8 +8,8 @@ #endif // tcp client helper -#include -#include
+#include +#include #include #include diff --git a/external/spdlog/include/details/thread_pool-inl.h b/r5dev/thirdparty/spdlog/include/details/thread_pool-inl.h similarity index 96% rename from external/spdlog/include/details/thread_pool-inl.h rename to r5dev/thirdparty/spdlog/include/details/thread_pool-inl.h index 02bf6b2b..5ce306b6 100644 --- a/external/spdlog/include/details/thread_pool-inl.h +++ b/r5dev/thirdparty/spdlog/include/details/thread_pool-inl.h @@ -4,10 +4,10 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include
+#include #endif -#include +#include #include namespace spdlog { diff --git a/external/spdlog/include/details/thread_pool.h b/r5dev/thirdparty/spdlog/include/details/thread_pool.h similarity index 94% rename from external/spdlog/include/details/thread_pool.h rename to r5dev/thirdparty/spdlog/include/details/thread_pool.h index 7cc0ab70..304a59d5 100644 --- a/external/spdlog/include/details/thread_pool.h +++ b/r5dev/thirdparty/spdlog/include/details/thread_pool.h @@ -3,9 +3,9 @@ #pragma once -#include
-#include
-#include
+#include +#include +#include #include #include diff --git a/external/spdlog/include/details/windows_include.h b/r5dev/thirdparty/spdlog/include/details/windows_include.h similarity index 100% rename from external/spdlog/include/details/windows_include.h rename to r5dev/thirdparty/spdlog/include/details/windows_include.h diff --git a/external/spdlog/include/fmt/bin_to_hex.h b/r5dev/thirdparty/spdlog/include/fmt/bin_to_hex.h similarity index 100% rename from external/spdlog/include/fmt/bin_to_hex.h rename to r5dev/thirdparty/spdlog/include/fmt/bin_to_hex.h diff --git a/external/spdlog/include/fmt/bundled/args.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/args.h similarity index 100% rename from external/spdlog/include/fmt/bundled/args.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/args.h diff --git a/external/spdlog/include/fmt/bundled/chrono.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/chrono.h similarity index 100% rename from external/spdlog/include/fmt/bundled/chrono.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/chrono.h diff --git a/external/spdlog/include/fmt/bundled/color.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/color.h similarity index 100% rename from external/spdlog/include/fmt/bundled/color.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/color.h diff --git a/external/spdlog/include/fmt/bundled/compile.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/compile.h similarity index 100% rename from external/spdlog/include/fmt/bundled/compile.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/compile.h diff --git a/external/spdlog/include/fmt/bundled/core.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/core.h similarity index 100% rename from external/spdlog/include/fmt/bundled/core.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/core.h diff --git a/external/spdlog/include/fmt/bundled/format-inl.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/format-inl.h similarity index 100% rename from external/spdlog/include/fmt/bundled/format-inl.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/format-inl.h diff --git a/external/spdlog/include/fmt/bundled/format.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/format.h similarity index 100% rename from external/spdlog/include/fmt/bundled/format.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/format.h diff --git a/external/spdlog/include/fmt/bundled/locale.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/locale.h similarity index 100% rename from external/spdlog/include/fmt/bundled/locale.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/locale.h diff --git a/external/spdlog/include/fmt/bundled/os.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/os.h similarity index 100% rename from external/spdlog/include/fmt/bundled/os.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/os.h diff --git a/external/spdlog/include/fmt/bundled/ostream.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/ostream.h similarity index 100% rename from external/spdlog/include/fmt/bundled/ostream.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/ostream.h diff --git a/external/spdlog/include/fmt/bundled/printf.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/printf.h similarity index 100% rename from external/spdlog/include/fmt/bundled/printf.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/printf.h diff --git a/external/spdlog/include/fmt/bundled/ranges.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/ranges.h similarity index 100% rename from external/spdlog/include/fmt/bundled/ranges.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/ranges.h diff --git a/external/spdlog/include/fmt/bundled/xchar.h b/r5dev/thirdparty/spdlog/include/fmt/bundled/xchar.h similarity index 100% rename from external/spdlog/include/fmt/bundled/xchar.h rename to r5dev/thirdparty/spdlog/include/fmt/bundled/xchar.h diff --git a/external/spdlog/include/fmt/chrono.h b/r5dev/thirdparty/spdlog/include/fmt/chrono.h similarity index 100% rename from external/spdlog/include/fmt/chrono.h rename to r5dev/thirdparty/spdlog/include/fmt/chrono.h diff --git a/external/spdlog/include/fmt/fmt.h b/r5dev/thirdparty/spdlog/include/fmt/fmt.h similarity index 74% rename from external/spdlog/include/fmt/fmt.h rename to r5dev/thirdparty/spdlog/include/fmt/fmt.h index 6779d6bb..5d3b0ba8 100644 --- a/external/spdlog/include/fmt/fmt.h +++ b/r5dev/thirdparty/spdlog/include/fmt/fmt.h @@ -19,10 +19,10 @@ #endif // enable the 'n' flag in for backward compatibility with fmt 6.x #define FMT_DEPRECATED_N_SPECIFIER -#include -#include +#include +#include #else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib -#include -#include +#include +#include #endif diff --git a/external/spdlog/include/fmt/ostr.h b/r5dev/thirdparty/spdlog/include/fmt/ostr.h similarity index 100% rename from external/spdlog/include/fmt/ostr.h rename to r5dev/thirdparty/spdlog/include/fmt/ostr.h diff --git a/external/spdlog/include/fmt/xchar.h b/r5dev/thirdparty/spdlog/include/fmt/xchar.h similarity index 100% rename from external/spdlog/include/fmt/xchar.h rename to r5dev/thirdparty/spdlog/include/fmt/xchar.h diff --git a/external/spdlog/include/formatter.h b/r5dev/thirdparty/spdlog/include/formatter.h similarity index 79% rename from external/spdlog/include/formatter.h rename to r5dev/thirdparty/spdlog/include/formatter.h index d80ea810..09228b60 100644 --- a/external/spdlog/include/formatter.h +++ b/r5dev/thirdparty/spdlog/include/formatter.h @@ -3,8 +3,8 @@ #pragma once -#include -#include
+#include +#include namespace spdlog { diff --git a/external/spdlog/include/fwd.h b/r5dev/thirdparty/spdlog/include/fwd.h similarity index 100% rename from external/spdlog/include/fwd.h rename to r5dev/thirdparty/spdlog/include/fwd.h diff --git a/external/spdlog/include/logger-inl.h b/r5dev/thirdparty/spdlog/include/logger-inl.h similarity index 97% rename from external/spdlog/include/logger-inl.h rename to r5dev/thirdparty/spdlog/include/logger-inl.h index f3014826..be81eed5 100644 --- a/external/spdlog/include/logger-inl.h +++ b/r5dev/thirdparty/spdlog/include/logger-inl.h @@ -7,9 +7,9 @@ #include #endif -#include -#include
-#include +#include +#include +#include #include diff --git a/external/spdlog/include/logger.h b/r5dev/thirdparty/spdlog/include/logger.h similarity index 98% rename from external/spdlog/include/logger.h rename to r5dev/thirdparty/spdlog/include/logger.h index 77934dc8..3178d3d1 100644 --- a/external/spdlog/include/logger.h +++ b/r5dev/thirdparty/spdlog/include/logger.h @@ -14,9 +14,9 @@ // The use of private formatter per sink provides the opportunity to cache some // formatted data, and support for different format per sink. -#include -#include
-#include
+#include +#include +#include #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT # include
diff --git a/external/spdlog/include/pattern_formatter-inl.h b/r5dev/thirdparty/spdlog/include/pattern_formatter-inl.h similarity index 99% rename from external/spdlog/include/pattern_formatter-inl.h rename to r5dev/thirdparty/spdlog/include/pattern_formatter-inl.h index 01752d40..56fa2f80 100644 --- a/external/spdlog/include/pattern_formatter-inl.h +++ b/r5dev/thirdparty/spdlog/include/pattern_formatter-inl.h @@ -7,11 +7,11 @@ #include #endif -#include
-#include
-#include
-#include -#include +#include +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/pattern_formatter.h b/r5dev/thirdparty/spdlog/include/pattern_formatter.h similarity index 94% rename from external/spdlog/include/pattern_formatter.h rename to r5dev/thirdparty/spdlog/include/pattern_formatter.h index aad1a265..1f07fd8b 100644 --- a/external/spdlog/include/pattern_formatter.h +++ b/r5dev/thirdparty/spdlog/include/pattern_formatter.h @@ -3,10 +3,10 @@ #pragma once -#include -#include
-#include
-#include +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/android_sink.h b/r5dev/thirdparty/spdlog/include/sinks/android_sink.h similarity index 91% rename from external/spdlog/include/sinks/android_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/android_sink.h index 7e359b64..15706340 100644 --- a/external/spdlog/include/sinks/android_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/android_sink.h @@ -5,11 +5,11 @@ #ifdef __ANDROID__ -#include
-#include
-#include
-#include -#include
+#include +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/ansicolor_sink-inl.h b/r5dev/thirdparty/spdlog/include/sinks/ansicolor_sink-inl.h similarity index 96% rename from external/spdlog/include/sinks/ansicolor_sink-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/ansicolor_sink-inl.h index 06d70f3f..40723e22 100644 --- a/external/spdlog/include/sinks/ansicolor_sink-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/ansicolor_sink-inl.h @@ -4,11 +4,11 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include -#include
+#include +#include namespace spdlog { namespace sinks { diff --git a/external/spdlog/include/sinks/ansicolor_sink.h b/r5dev/thirdparty/spdlog/include/sinks/ansicolor_sink.h similarity index 95% rename from external/spdlog/include/sinks/ansicolor_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/ansicolor_sink.h index 7d20d1d8..fa53c742 100644 --- a/external/spdlog/include/sinks/ansicolor_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/ansicolor_sink.h @@ -3,9 +3,10 @@ #pragma once -#include
-#include
-#include +#include +#include +#include + #include #include #include diff --git a/external/spdlog/include/sinks/base_sink-inl.h b/r5dev/thirdparty/spdlog/include/sinks/base_sink-inl.h similarity index 91% rename from external/spdlog/include/sinks/base_sink-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/base_sink-inl.h index d8ca467a..9c01965b 100644 --- a/external/spdlog/include/sinks/base_sink-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/base_sink-inl.h @@ -4,11 +4,11 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include -#include +#include +#include #include diff --git a/external/spdlog/include/sinks/base_sink.h b/r5dev/thirdparty/spdlog/include/sinks/base_sink.h similarity index 90% rename from external/spdlog/include/sinks/base_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/base_sink.h index d8bac0b5..ff3b46ed 100644 --- a/external/spdlog/include/sinks/base_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/base_sink.h @@ -9,9 +9,9 @@ // implementers.. // -#include -#include
-#include +#include +#include +#include namespace spdlog { namespace sinks { diff --git a/external/spdlog/include/sinks/basic_file_sink-inl.h b/r5dev/thirdparty/spdlog/include/sinks/basic_file_sink-inl.h similarity index 85% rename from external/spdlog/include/sinks/basic_file_sink-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/basic_file_sink-inl.h index de5997f0..0a50f777 100644 --- a/external/spdlog/include/sinks/basic_file_sink-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/basic_file_sink-inl.h @@ -4,11 +4,11 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include -#include
+#include +#include namespace spdlog { namespace sinks { diff --git a/external/spdlog/include/sinks/basic_file_sink.h b/r5dev/thirdparty/spdlog/include/sinks/basic_file_sink.h similarity index 86% rename from external/spdlog/include/sinks/basic_file_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/basic_file_sink.h index 30f0365d..06e91706 100644 --- a/external/spdlog/include/sinks/basic_file_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/basic_file_sink.h @@ -3,10 +3,10 @@ #pragma once -#include
-#include
-#include -#include
+#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/daily_file_sink.h b/r5dev/thirdparty/spdlog/include/sinks/daily_file_sink.h similarity index 94% rename from external/spdlog/include/sinks/daily_file_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/daily_file_sink.h index 0bdfd824..5261c5e5 100644 --- a/external/spdlog/include/sinks/daily_file_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/daily_file_sink.h @@ -3,15 +3,15 @@ #pragma once -#include -#include
-#include
-#include -#include -#include -#include
-#include
-#include
+#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/dist_sink.h b/r5dev/thirdparty/spdlog/include/sinks/dist_sink.h similarity index 91% rename from external/spdlog/include/sinks/dist_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/dist_sink.h index 1053ac6d..76a1fbe9 100644 --- a/external/spdlog/include/sinks/dist_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/dist_sink.h @@ -3,10 +3,10 @@ #pragma once -#include "base_sink.h" -#include
-#include
-#include +#include "thirdparty/spdlog/include/base_sink.h" +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/dup_filter_sink.h b/r5dev/thirdparty/spdlog/include/sinks/dup_filter_sink.h similarity index 94% rename from external/spdlog/include/sinks/dup_filter_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/dup_filter_sink.h index 836f5cb2..0e42ceea 100644 --- a/external/spdlog/include/sinks/dup_filter_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/dup_filter_sink.h @@ -3,9 +3,9 @@ #pragma once -#include "dist_sink.h" -#include
-#include
+#include "thirdparty/spdlog/include/dist_sink.h" +#include +#include #include #include diff --git a/external/spdlog/include/sinks/hourly_file_sink.h b/r5dev/thirdparty/spdlog/include/sinks/hourly_file_sink.h similarity index 92% rename from external/spdlog/include/sinks/hourly_file_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/hourly_file_sink.h index bf0c914d..79356721 100644 --- a/external/spdlog/include/sinks/hourly_file_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/hourly_file_sink.h @@ -3,14 +3,14 @@ #pragma once -#include -#include
-#include
-#include -#include -#include
-#include
-#include
+#include +#include +#include +#include +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/mongo_sink.h b/r5dev/thirdparty/spdlog/include/sinks/mongo_sink.h similarity index 92% rename from external/spdlog/include/sinks/mongo_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/mongo_sink.h index 0047b15e..b01eee10 100644 --- a/external/spdlog/include/sinks/mongo_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/mongo_sink.h @@ -10,10 +10,10 @@ // http://mongocxx.org/mongocxx-v3/installation/ // -#include "spdlog/common.h" -#include "spdlog/details/log_msg.h" -#include "spdlog/sinks/base_sink.h" -#include
+#include "thirdparty/spdlog/include/spdlog/common.h" +#include "thirdparty/spdlog/include/spdlog/details/log_msg.h" +#include "thirdparty/spdlog/include/spdlog/sinks/base_sink.h" +#include #include #include diff --git a/external/spdlog/include/sinks/msvc_sink.h b/r5dev/thirdparty/spdlog/include/sinks/msvc_sink.h similarity index 90% rename from external/spdlog/include/sinks/msvc_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/msvc_sink.h index 797f2fcd..e235bd65 100644 --- a/external/spdlog/include/sinks/msvc_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/msvc_sink.h @@ -5,8 +5,8 @@ #if defined(_WIN32) -#include
-#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/null_sink.h b/r5dev/thirdparty/spdlog/include/sinks/null_sink.h similarity index 86% rename from external/spdlog/include/sinks/null_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/null_sink.h index 5eaf9e3f..0c8b144c 100644 --- a/external/spdlog/include/sinks/null_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/null_sink.h @@ -3,9 +3,9 @@ #pragma once -#include
-#include -#include
+#include +#include +#include #include diff --git a/external/spdlog/include/sinks/ostream_sink.h b/r5dev/thirdparty/spdlog/include/sinks/ostream_sink.h similarity index 91% rename from external/spdlog/include/sinks/ostream_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/ostream_sink.h index d798568a..660bb0c3 100644 --- a/external/spdlog/include/sinks/ostream_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/ostream_sink.h @@ -3,8 +3,8 @@ #pragma once -#include
-#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/qt_sinks.h b/r5dev/thirdparty/spdlog/include/sinks/qt_sinks.h similarity index 95% rename from external/spdlog/include/sinks/qt_sinks.h rename to r5dev/thirdparty/spdlog/include/sinks/qt_sinks.h index c2d02506..326f68a4 100644 --- a/external/spdlog/include/sinks/qt_sinks.h +++ b/r5dev/thirdparty/spdlog/include/sinks/qt_sinks.h @@ -8,10 +8,10 @@ // etc) Building and using requires Qt library. // -#include "spdlog/common.h" -#include "spdlog/details/log_msg.h" -#include "spdlog/details/synchronous_factory.h" -#include "spdlog/sinks/base_sink.h" +#include "thirdparty/spdlog/include/spdlog/common.h" +#include "thirdparty/spdlog/include/spdlog/details/log_msg.h" +#include "thirdparty/spdlog/include/spdlog/details/synchronous_factory.h" +#include "thirdparty/spdlog/include/spdlog/sinks/base_sink.h" #include #include diff --git a/external/spdlog/include/sinks/ringbuffer_sink.h b/r5dev/thirdparty/spdlog/include/sinks/ringbuffer_sink.h similarity index 88% rename from external/spdlog/include/sinks/ringbuffer_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/ringbuffer_sink.h index 1ee3f691..1864be0e 100644 --- a/external/spdlog/include/sinks/ringbuffer_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/ringbuffer_sink.h @@ -3,10 +3,10 @@ #pragma once -#include "spdlog/sinks/base_sink.h" -#include "spdlog/details/circular_q.h" -#include "spdlog/details/log_msg_buffer.h" -#include "spdlog/details/null_mutex.h" +#include "thirdparty/spdlog/include/spdlog/sinks/base_sink.h" +#include "thirdparty/spdlog/include/spdlog/details/circular_q.h" +#include "thirdparty/spdlog/include/spdlog/details/log_msg_buffer.h" +#include "thirdparty/spdlog/include/spdlog/details/null_mutex.h" #include #include diff --git a/external/spdlog/include/sinks/rotating_file_sink-inl.h b/r5dev/thirdparty/spdlog/include/sinks/rotating_file_sink-inl.h similarity index 93% rename from external/spdlog/include/sinks/rotating_file_sink-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/rotating_file_sink-inl.h index 94622d9a..d0bbdeab 100644 --- a/external/spdlog/include/sinks/rotating_file_sink-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/rotating_file_sink-inl.h @@ -4,14 +4,14 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include +#include -#include
-#include
-#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/rotating_file_sink.h b/r5dev/thirdparty/spdlog/include/sinks/rotating_file_sink.h similarity index 90% rename from external/spdlog/include/sinks/rotating_file_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/rotating_file_sink.h index 097454a0..b2e73902 100644 --- a/external/spdlog/include/sinks/rotating_file_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/rotating_file_sink.h @@ -3,10 +3,10 @@ #pragma once -#include -#include
-#include
-#include
+#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/sink-inl.h b/r5dev/thirdparty/spdlog/include/sinks/sink-inl.h similarity index 87% rename from external/spdlog/include/sinks/sink-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/sink-inl.h index 31615c4b..47170213 100644 --- a/external/spdlog/include/sinks/sink-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/sink-inl.h @@ -4,10 +4,10 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include +#include SPDLOG_INLINE bool spdlog::sinks::sink::should_log(spdlog::level::level_enum msg_level) const { diff --git a/external/spdlog/include/sinks/sink.h b/r5dev/thirdparty/spdlog/include/sinks/sink.h similarity index 88% rename from external/spdlog/include/sinks/sink.h rename to r5dev/thirdparty/spdlog/include/sinks/sink.h index 20d607ae..4f5939b8 100644 --- a/external/spdlog/include/sinks/sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/sink.h @@ -3,8 +3,8 @@ #pragma once -#include
-#include +#include +#include namespace spdlog { diff --git a/external/spdlog/include/sinks/stdout_color_sinks-inl.h b/r5dev/thirdparty/spdlog/include/sinks/stdout_color_sinks-inl.h similarity index 94% rename from external/spdlog/include/sinks/stdout_color_sinks-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/stdout_color_sinks-inl.h index ac4845d3..87663d8c 100644 --- a/external/spdlog/include/sinks/stdout_color_sinks-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/stdout_color_sinks-inl.h @@ -4,7 +4,7 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif #include diff --git a/external/spdlog/include/sinks/stdout_color_sinks.h b/r5dev/thirdparty/spdlog/include/sinks/stdout_color_sinks.h similarity index 85% rename from external/spdlog/include/sinks/stdout_color_sinks.h rename to r5dev/thirdparty/spdlog/include/sinks/stdout_color_sinks.h index 5130631a..1fac52cc 100644 --- a/external/spdlog/include/sinks/stdout_color_sinks.h +++ b/r5dev/thirdparty/spdlog/include/sinks/stdout_color_sinks.h @@ -4,12 +4,12 @@ #pragma once #ifdef _WIN32 -#include +#include #else -#include +#include #endif -#include
+#include namespace spdlog { namespace sinks { @@ -41,5 +41,5 @@ std::shared_ptr stderr_color_st(const std::string &logger_name, color_mo } // namespace spdlog #ifdef SPDLOG_HEADER_ONLY -#include "stdout_color_sinks-inl.h" +#include "thirdparty/spdlog/include/stdout_color_sinks-inl.h" #endif diff --git a/external/spdlog/include/sinks/stdout_sinks-inl.h b/r5dev/thirdparty/spdlog/include/sinks/stdout_sinks-inl.h similarity index 94% rename from external/spdlog/include/sinks/stdout_sinks-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/stdout_sinks-inl.h index 7458e541..9a36e0b2 100644 --- a/external/spdlog/include/sinks/stdout_sinks-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/stdout_sinks-inl.h @@ -4,17 +4,17 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include
-#include +#include +#include #include #ifdef _WIN32 // under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675) // so instead we use ::FileWrite -#include
+#include #ifndef _USING_V110_SDK71_ // fileapi.h doesnt exist in winxp #include // WriteFile (..) diff --git a/external/spdlog/include/sinks/stdout_sinks.h b/r5dev/thirdparty/spdlog/include/sinks/stdout_sinks.h similarity index 90% rename from external/spdlog/include/sinks/stdout_sinks.h rename to r5dev/thirdparty/spdlog/include/sinks/stdout_sinks.h index 33c7d84f..4e9f542a 100644 --- a/external/spdlog/include/sinks/stdout_sinks.h +++ b/r5dev/thirdparty/spdlog/include/sinks/stdout_sinks.h @@ -3,13 +3,13 @@ #pragma once -#include
-#include
-#include +#include +#include +#include #include #ifdef _WIN32 -#include
+#include #endif namespace spdlog { diff --git a/external/spdlog/include/sinks/syslog_sink.h b/r5dev/thirdparty/spdlog/include/sinks/syslog_sink.h similarity index 95% rename from external/spdlog/include/sinks/syslog_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/syslog_sink.h index 206cec43..24d9c4b5 100644 --- a/external/spdlog/include/sinks/syslog_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/syslog_sink.h @@ -3,9 +3,9 @@ #pragma once -#include -#include
-#include
+#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/systemd_sink.h b/r5dev/thirdparty/spdlog/include/sinks/systemd_sink.h similarity index 94% rename from external/spdlog/include/sinks/systemd_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/systemd_sink.h index c92d7076..c17ccfb3 100644 --- a/external/spdlog/include/sinks/systemd_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/systemd_sink.h @@ -3,9 +3,9 @@ #pragma once -#include -#include
-#include
+#include +#include +#include #include #ifndef SD_JOURNAL_SUPPRESS_LOCATION diff --git a/external/spdlog/include/sinks/tcp_sink.h b/r5dev/thirdparty/spdlog/include/sinks/tcp_sink.h similarity index 87% rename from external/spdlog/include/sinks/tcp_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/tcp_sink.h index a336ff41..953347e3 100644 --- a/external/spdlog/include/sinks/tcp_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/tcp_sink.h @@ -3,13 +3,13 @@ #pragma once -#include -#include -#include
+#include +#include +#include #ifdef _WIN32 -#include
+#include #else -#include
+#include #endif #include diff --git a/external/spdlog/include/sinks/win_eventlog_sink.h b/r5dev/thirdparty/spdlog/include/sinks/win_eventlog_sink.h similarity index 97% rename from external/spdlog/include/sinks/win_eventlog_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/win_eventlog_sink.h index 1fc57486..eb7c1357 100644 --- a/external/spdlog/include/sinks/win_eventlog_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/win_eventlog_sink.h @@ -30,11 +30,11 @@ Windows Registry Editor Version 5.00 #pragma once -#include
-#include +#include +#include -#include
-#include +#include +#include #include #include diff --git a/external/spdlog/include/sinks/wincolor_sink-inl.h b/r5dev/thirdparty/spdlog/include/sinks/wincolor_sink-inl.h similarity index 96% rename from external/spdlog/include/sinks/wincolor_sink-inl.h rename to r5dev/thirdparty/spdlog/include/sinks/wincolor_sink-inl.h index 422e74ac..4a40b218 100644 --- a/external/spdlog/include/sinks/wincolor_sink-inl.h +++ b/r5dev/thirdparty/spdlog/include/sinks/wincolor_sink-inl.h @@ -4,14 +4,14 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include
+#include #include -#include -#include +#include +#include namespace spdlog { namespace sinks { diff --git a/external/spdlog/include/sinks/wincolor_sink.h b/r5dev/thirdparty/spdlog/include/sinks/wincolor_sink.h similarity index 92% rename from external/spdlog/include/sinks/wincolor_sink.h rename to r5dev/thirdparty/spdlog/include/sinks/wincolor_sink.h index 34ee1ef2..43c669e8 100644 --- a/external/spdlog/include/sinks/wincolor_sink.h +++ b/r5dev/thirdparty/spdlog/include/sinks/wincolor_sink.h @@ -3,10 +3,10 @@ #pragma once -#include -#include
-#include
-#include +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/spdlog-inl.h b/r5dev/thirdparty/spdlog/include/spdlog-inl.h similarity index 95% rename from external/spdlog/include/spdlog-inl.h rename to r5dev/thirdparty/spdlog/include/spdlog-inl.h index 797bbdae..4eb2fc32 100644 --- a/external/spdlog/include/spdlog-inl.h +++ b/r5dev/thirdparty/spdlog/include/spdlog-inl.h @@ -4,11 +4,11 @@ #pragma once #ifndef SPDLOG_HEADER_ONLY -#include +#include #endif -#include -#include +#include +#include namespace spdlog { diff --git a/external/spdlog/include/spdlog.h b/r5dev/thirdparty/spdlog/include/spdlog.h similarity index 97% rename from external/spdlog/include/spdlog.h rename to r5dev/thirdparty/spdlog/include/spdlog.h index 7abda7fb..077705d2 100644 --- a/external/spdlog/include/spdlog.h +++ b/r5dev/thirdparty/spdlog/include/spdlog.h @@ -9,11 +9,11 @@ #pragma once -#include -#include
-#include -#include -#include
+#include +#include +#include +#include +#include #include #include diff --git a/external/spdlog/include/stopwatch.h b/r5dev/thirdparty/spdlog/include/stopwatch.h similarity index 97% rename from external/spdlog/include/stopwatch.h rename to r5dev/thirdparty/spdlog/include/stopwatch.h index ca36aad6..da2621da 100644 --- a/external/spdlog/include/stopwatch.h +++ b/r5dev/thirdparty/spdlog/include/stopwatch.h @@ -3,7 +3,7 @@ #pragma once -#include +#include // Stopwatch support for spdlog (using std::chrono::steady_clock). // Displays elapsed seconds since construction as double. diff --git a/external/spdlog/include/tweakme.h b/r5dev/thirdparty/spdlog/include/tweakme.h similarity index 100% rename from external/spdlog/include/tweakme.h rename to r5dev/thirdparty/spdlog/include/tweakme.h diff --git a/external/spdlog/include/version.h b/r5dev/thirdparty/spdlog/include/version.h similarity index 100% rename from external/spdlog/include/version.h rename to r5dev/thirdparty/spdlog/include/version.h diff --git a/r5dev/tier0/ConCommand.cpp b/r5dev/tier0/ConCommand.cpp new file mode 100644 index 00000000..f3793445 --- /dev/null +++ b/r5dev/tier0/ConCommand.cpp @@ -0,0 +1,118 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "tier0/completion.h" +#include "tier0/ConCommand.h" +#include "client/client.h" +#include "engine/sys_utils.h" + +//----------------------------------------------------------------------------- +// Purpose: test each ConCommand query before execution +// Input : *cmd - flag +// Output : true if execution is not permitted, false if permitted +//----------------------------------------------------------------------------- +bool HConCommand_IsFlagSet(ConCommandBase* pCommandBase, int nFlag) +{ + if (cm_debug_cmdquery->m_pParent->m_iValue > 0) + { + printf("--------------------------------------------------\n"); + printf(" Flaged: %08X\n", pCommandBase->m_nFlags); + } + // Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY + pCommandBase->m_nFlags &= 0xFFFFBFFD; + if (cm_debug_cmdquery->m_pParent->m_iValue > 0) + { + printf(" Masked: %08X\n", pCommandBase->m_nFlags); + printf(" Verify: %08X\n", nFlag); + printf("--------------------------------------------------\n"); + } + if (nFlag & 0x80000 && cm_return_false_cmdquery_all->m_pParent->m_iValue <= 0) + { + return ConCommand_IsFlagSet(pCommandBase, nFlag); + } + if (cm_return_false_cmdquery_all->m_pParent->m_iValue > 0) + { + // Returning false on all queries may cause problems. + return false; + } + if (cm_return_false_cmdquery_dev_cheat->m_pParent->m_iValue > 0) + { + // Return false on every FCVAR_DEVELOPMENTONLY || FCVAR_CHEAT query. + return (pCommandBase->m_nFlags & nFlag) != 0; + } + // Default behaviour. + return ConCommand_IsFlagSet(pCommandBase, nFlag); +} + +//----------------------------------------------------------------------------- +// Purpose: register ConCommand's +//----------------------------------------------------------------------------- +void* ConCommand_RegisterCommand(const char* szName, const char* szHelpString, int nFlags, void* pCallback, void* pCommandCompletionCallback) +{ + void* pCommand = reinterpret_cast(MemAlloc_Wrapper(0x68)); // Allocate new memory with StdMemAlloc else we crash. + memset(pCommand, 0, 0x68); // Set all to null. + std::uintptr_t pCommandBase = reinterpret_cast(pCommand); // To ptr. + + *(void**)pCommandBase = g_pConCommandVtable.RCast(); // 0x00 to ConCommand vtable. + *(const char**)(pCommandBase + 0x18) = szName; // 0x18 to ConCommand Name. + *(const char**)(pCommandBase + 0x20) = szHelpString; // 0x20 to ConCommand help string. + *(std::int32_t*)(pCommandBase + 0x38) = nFlags; // 0x38 to ConCommand Flags. + *(void**)(pCommandBase + 0x40) = p_ConCommand_NullSub.RCast(); // 0x40 Nullsub since every concommand has it. + *(void**)(pCommandBase + 0x50) = pCallback; // 0x50 has function callback. + *(std::int32_t*)(pCommandBase + 0x60) = 2; // 0x60 Set to use callback and newcommand callback. + + if (pCommandCompletionCallback) // callback after execution desired? + { + *(void**)(pCommandBase + 0x58) = pCommandCompletionCallback; // 0x58 to our callback after execution. + } + else + { + *(void**)(pCommandBase + 0x58) = p_ConCommand_CallbackCompletion.RCast(); // 0x58 nullsub. + } + + p_ConCommand_RegisterConCommand.RCast()((void*)pCommandBase); // Register command in ConVarAccessor. + + return pCommand; +} + +//----------------------------------------------------------------------------- +// Purpose: ConCommand definitions to be registered +//----------------------------------------------------------------------------- +void ConCommand_InitConCommand() +{ + //------------------------------------------------------------------------- + // SERVER DLL | + void* sv_kick = ConCommand_RegisterCommand("sv_kick", "Kick a client from the server by name. | Usage: kick .", 0, _Kick_f_CompletionFunc, nullptr); + void* sv_kickid = ConCommand_RegisterCommand("sv_kickid", "Kick a client from the server by UserID or OriginID | Usage: kickid /.", 0, _KickID_f_CompletionFunc, nullptr); + void* sv_ban = ConCommand_RegisterCommand("sv_ban", "Bans a client from the server by name. | Usage: ban .", 0, _Ban_f_CompletionFunc, nullptr); + void* sv_banid = ConCommand_RegisterCommand("sv_banid", "Bans a client from the server by OriginID, UserID or IPAddress | Usage: banid //.", 0, _BanID_f_CompletionFunc, nullptr); + void* sv_unban = ConCommand_RegisterCommand("sv_unban", "Unbans a client from the Server by IPAddress or OriginID | Usage: unban /.", 0, _Unban_f_CompletionFunc, nullptr); + void* sv_reloadbanlist = ConCommand_RegisterCommand("sv_reloadbanlist", "Reloads the ban list from the disk.", 0, _ReloadBanList_f_CompletionFunc, nullptr); +#ifndef DEDICATED + //------------------------------------------------------------------------- + // CLIENT DLL | + void* cl_showconsole = ConCommand_RegisterCommand("cl_showconsole", "Opens the game console.", 0, _CGameConsole_f_CompletionFunc, nullptr); + void* cl_showbrowser = ConCommand_RegisterCommand("cl_showbrowser", "Opens the server browser.", 0, _CCompanion_f_CompletionFunc, nullptr); +#endif // !DEDICATED + //------------------------------------------------------------------------- + // FILESYSTEM API | + void* fs_decompress_pak = ConCommand_RegisterCommand("fs_decompress_pak", "Decompresses user specified 'vpk_dir' file.", 0, _VPK_Decompress_f_CompletionFunc, nullptr); + //------------------------------------------------------------------------- + // RTECH API | + void* rtech_toguid = ConCommand_RegisterCommand("rtech_toguid", "Calculates the GUID from input data.", 0, _RTech_GenerateGUID_f_CompletionFunc, nullptr); + void* rtech_decompress = ConCommand_RegisterCommand("rtech_decompress", "Decompresses user specified 'RPak' file.", 0, _RTech_Decompress_f_CompletionFunc, nullptr); + //------------------------------------------------------------------------- + // NETCHANNEL | + void* net_toggletrace = ConCommand_RegisterCommand("net_toggletrace", "Logs the sending and receiving datagram to a file on the disk.", 0, _NET_TraceNetChan_f_CompletionFunc, nullptr); + void* net_setkey = ConCommand_RegisterCommand("net_setkey", "Sets user specified base64 net key.", 0, _NET_SetKey_f_CompletionFunc, nullptr); + void* net_generatekey = ConCommand_RegisterCommand("net_generatekey", "Generates and sets a random base64 net key.", 0, _NET_GenerateKey_f_CompletionFunc, nullptr); +} + +void ConCommand_Attach() +{ + DetourAttach((LPVOID*)&ConCommand_IsFlagSet, &HConCommand_IsFlagSet); +} + +void ConCommand_Detach() +{ + DetourDetach((LPVOID*)&ConCommand_IsFlagSet, &HConCommand_IsFlagSet); +} diff --git a/r5dev/tier0/ConCommand.h b/r5dev/tier0/ConCommand.h new file mode 100644 index 00000000..05b0e2dd --- /dev/null +++ b/r5dev/tier0/ConCommand.h @@ -0,0 +1,133 @@ +#pragma once + +class CCommand +{ +private: + enum + { + COMMAND_MAX_ARGC = 64, + COMMAND_MAX_LENGTH = 512, + }; + +public: + CCommand() = delete; + + inline int MaxCommandLength() + { + return COMMAND_MAX_LENGTH - 1; + } + + inline std::int64_t ArgC() const + { + return m_nArgc; + } + + inline const char** ArgV() const + { + return m_nArgc ? (const char**)m_ppArgv : NULL; + } + + inline const char* ArgS() const + { + return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : ""; + } + + inline const char* GetCommandString() const + { + return m_nArgc ? m_pArgSBuffer : ""; + } + + inline const char* Arg(int nIndex) const + { + // FIXME: Many command handlers appear to not be particularly careful + // about checking for valid argc range. For now, we're going to + // do the extra check and return an empty string if it's out of range + if (nIndex < 0 || nIndex >= m_nArgc) + { + return ""; + } + return m_ppArgv[nIndex]; + } + + inline const char* operator[](int nIndex) const + { + return Arg(nIndex); + } + +private: + std::int64_t m_nArgc; + std::int64_t m_nArgv0Size; + char m_pArgSBuffer[COMMAND_MAX_LENGTH]; + char m_pArgvBuffer[COMMAND_MAX_LENGTH]; + const char* m_ppArgv[COMMAND_MAX_ARGC]; +}; + +class ConCommand +{ + // TODO +}; + +class ConCommandBase +{ +public: + void* m_pConCommandBaseVTable; //0x0000 + ConCommandBase* m_pNext; //0x0008 + bool m_bRegistered; //0x0010 +private: + char pad_0011[7]; //0x0011 +public: + const char* m_pszName; //0x0018 + const char* m_pszHelpString; //0x0020 +private: + char pad_0028[16]; //0x0028 +public: + int m_nFlags; //0x0038 +private: + char pad_003C[4]; //0x003C +}; //Size: 0x0038 + +namespace +{ + /* ==== CONCOMMAND ====================================================================================================================================================== */ + ADDRESS p_ConCommand_IsFlagSet = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x85\x51\x38\x0F\x95\xC0\xC3", "xxxxxxx"); + bool (*ConCommand_IsFlagSet)(ConCommandBase* cmd, int flag) = (bool (*)(ConCommandBase*, int))p_ConCommand_IsFlagSet.GetPtr(); /*85 51 38 0F 95 C0 C3*/ + + ADDRESS p_ConCommand_CMaterialSystemCmdInit = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x74\x24\x00\x48\x89\x7C\x24\x00\x55\x41\x54\x41\x55\x41\x56\x41\x57\x48\x8B\xEC\x48\x83\xEC\x50\x48\x8B\x15\x00\x00\x00\x00", "xxxx?xxxx?xxxx?xxxxxxxxxxxxxxxxxxx????"); + ConCommand*(*ConCommand_CMaterialSystemCmdInit)() = (ConCommand* (*)())p_ConCommand_CMaterialSystemCmdInit.GetPtr(); + + ADDRESS p_ConCommand_NullSub = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\xC2\x00\x00\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\x40\x53\x48\x83\xEC\x20\x48\x8D\x05\x00\x00\x00\x00", "xxxxxxxxxxxxxxxxxxxxxxxxx????"); + void (*ConCommand_NullSub)() = (void (*)())p_ConCommand_NullSub.GetPtr(); /*C2 00 00 CC CC CC CC CC CC CC CC CC CC CC CC CC 40 53 48 83 EC 20 48 8D 05 ?? ?? ?? ??*/ + + ADDRESS p_ConCommand_CallbackCompletion = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x33\xC0\xC3\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\x80\x49\x68\x08", "xxxxxxxxxxxxxxxxxxxx"); + void* (*ConCommand_CallbackCompletion)(struct _exception* _exc) = (void* (*)(struct _exception*))p_ConCommand_CallbackCompletion.GetPtr(); /*33 C0 C3 CC CC CC CC CC CC CC CC CC CC CC CC CC 80 49 68 08*/ /*UserMathErrorFunction*/ + + ADDRESS p_ConCommand_RegisterConCommand = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\xD1\x48\x8B\x0D\x00\x00\x00\x00\x48\x85\xC9\x74\x06", "xxxxxx????xxxxx"); + void* (*ConCommand_RegisterConCommand)(ConCommandBase* pCommandBase) = (void* (*)(ConCommandBase*))p_ConCommand_RegisterConCommand.GetPtr(); /*48 8B D1 48 8B 0D ?? ?? ?? ?? 48 85 C9 74 06 */ + + static ADDRESS g_pConCommandVtable = p_ConCommand_CMaterialSystemCmdInit.FindPatternSelf("4C 8D 25", ADDRESS::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7); +} + +/////////////////////////////////////////////////////////////////////////////// +bool HConCommand_IsFlagSet(ConCommandBase* pCommand, int nFlag); +void ConCommand_InitConCommand(); + +void ConCommand_Attach(); +void ConCommand_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HConCommand : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: ConCommand::IsFlagSet : 0x" << std::hex << std::uppercase << p_ConCommand_IsFlagSet.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: ConCommand::CMaterialSystemCmdInit : 0x" << std::hex << std::uppercase << p_ConCommand_CMaterialSystemCmdInit.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: ConCommand::NullSub : 0x" << std::hex << std::uppercase << p_ConCommand_NullSub.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: ConCommand::CallbackCompletion : 0x" << std::hex << std::uppercase << p_ConCommand_CallbackCompletion.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: ConCommand::RegisterConCommand : 0x" << std::hex << std::uppercase << p_ConCommand_RegisterConCommand.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pConCommandVtable : 0x" << std::hex << std::uppercase << g_pConCommandVtable.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HConCommand); diff --git a/r5dev/tier0/IConVar.cpp b/r5dev/tier0/IConVar.cpp new file mode 100644 index 00000000..f2d361bf --- /dev/null +++ b/r5dev/tier0/IConVar.cpp @@ -0,0 +1,144 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "tier0/IConVar.h" +#include "engine/sys_utils.h" +#include "engine/sys_dll2.h" + +//----------------------------------------------------------------------------- +// Purpose: test each ConVar query before setting the cvar +// Input : **cvar - flag +// Output : true if change is not permitted, false if permitted +//----------------------------------------------------------------------------- +bool HIConVar_IsFlagSet(ConVar* pConVar, int nFlags) +{ + if (cm_debug_cmdquery->m_pParent->m_iValue > 0) + { + printf("--------------------------------------------------\n"); + printf(" Flaged: %08X\n", pConVar->m_ConCommandBase.m_nFlags); + } + // Mask off FCVAR_CHEATS and FCVAR_DEVELOPMENTONLY + pConVar->m_ConCommandBase.m_nFlags &= 0xFFFFBFFD; + if (cm_debug_cmdquery->m_pParent->m_iValue > 0) + { + printf(" Masked: %08X\n", pConVar->m_ConCommandBase.m_nFlags); + printf(" Verify: %08X\n", nFlags); + printf("--------------------------------------------------\n"); + } + if (nFlags & 0x80000 && cm_return_false_cmdquery_all->m_pParent->m_iValue <= 0) + { + return IConVar_IsFlagSet(pConVar, nFlags); + } + if (cm_return_false_cmdquery_all->m_pParent->m_iValue > 0) + { + // Returning false on all queries may cause problems. + return false; + } + if (cm_return_false_cmdquery_dev_cheat->m_pParent->m_iValue > 0) + { + // Return false on every FCVAR_DEVELOPMENTONLY || FCVAR_CHEAT query. + return (pConVar->m_ConCommandBase.m_nFlags & nFlags) != 0; + } + // Default behaviour. + return IConVar_IsFlagSet(pConVar, nFlags); +} + +//----------------------------------------------------------------------------- +// Purpose: register ConVar's +//----------------------------------------------------------------------------- +ConVar* IConVar_RegisterConVar(const char* szName, const char* szDefaultValue, int nFlags, const char* szHelpString, bool bMin, float fMin, bool bMax, float fMax, void* pCallback, void* unk) +{ + ConVar* allocatedConvar = reinterpret_cast(MemAlloc_Wrapper(0xA0)); // Allocate new memory with StdMemAlloc else we crash. + memset(allocatedConvar, 0, 0xA0); // Set all to null. + std::uintptr_t cvarPtr = reinterpret_cast(allocatedConvar); // To ptr. + + *(void**)(cvarPtr + 0x40) = g_pIConVarVtable.RCast(); // 0x40 to ICvar table. + *(void**)cvarPtr = g_pConVarVtable.RCast(); // 0x0 to ConVar vtable. + + p_ConVar_Register.RCast() // Call to create ConVar. + (allocatedConvar, szName, szDefaultValue, nFlags, szHelpString, bMin, fMin, bMax, fMax, pCallback, unk); + + return allocatedConvar; // Return allocated ConVar. +} + +//----------------------------------------------------------------------------- +// Purpose: convar definitions to be registered +//----------------------------------------------------------------------------- +void IConVar_InitConVar() +{ + //------------------------------------------------------------------------- + // ENGINE | + cm_debug_cmdquery = IConVar_RegisterConVar("cm_debug_cmdquery", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Prints the flags of each ConVar/ConCommand query to the console ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); + cm_return_false_cmdquery_all = IConVar_RegisterConVar("cm_return_false_cmdquery_all", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Returns false on every ConVar/ConCommand query ( !warning! ).", false, 0.f, false, 0.f, nullptr, nullptr); + cm_return_false_cmdquery_dev_cheat = IConVar_RegisterConVar("cm_return_false_cmdquery_dev_cheat", "1", FCVAR_RELEASE, "Returns false on all FCVAR_DEVELOPMENTONLY and FCVAR_CHEAT ConVar/ConCommand queries ( !warning! ).", false, 0.f, false, 0.f, nullptr, nullptr); + //------------------------------------------------------------------------- + // SERVER | + sv_showconnecting = IConVar_RegisterConVar("sv_showconnecting", "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Logs information about the connecting client to the console.", false, 0.f, false, 0.f, nullptr, nullptr); + //------------------------------------------------------------------------- + // CLIENT | + cl_drawconsoleoverlay = IConVar_RegisterConVar("cl_drawconsoleoverlay", "1", FCVAR_RELEASE, "Draw the console overlay at the top of the screen.", false, 0.f, false, 0.f, nullptr, nullptr); + cl_consoleoverlay_lines = IConVar_RegisterConVar("cl_consoleoverlay_lines", "3", FCVAR_RELEASE, "Number of lines of console output to draw.", false, 0.f, false, 0.f, nullptr, nullptr); + cl_consoleoverlay_offset_x = IConVar_RegisterConVar("cl_consoleoverlay_offset_x", "10", FCVAR_RELEASE, "X offset for console overlay.", false, 1.f, false, 50.f, nullptr, nullptr); + cl_consoleoverlay_offset_y = IConVar_RegisterConVar("cl_consoleoverlay_offset_y", "10", FCVAR_RELEASE, "Y offset for console overlay.", false, 1.f, false, 50.f, nullptr, nullptr); + //------------------------------------------------------------------------- + // FILESYSTEM | + fs_warning_level_native = IConVar_RegisterConVar("fs_warning_level_native", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Set the filesystem warning level ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); + fs_packedstore_entryblock_stats = IConVar_RegisterConVar("fs_packedstore_entryblock_stats", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "If set to 1, prints the stats of each file entry in the VPK during decompression ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); + //------------------------------------------------------------------------- + // FILESYSTEM | + mat_showdxoutput = IConVar_RegisterConVar("mat_showdxoutput", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Shows debug output for the DirectX system.", false, 0.f, false, 0.f, nullptr, nullptr); + //------------------------------------------------------------------------- + // SQUIRREL | + sq_showrsonloading = IConVar_RegisterConVar("sq_showrsonloading", "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Logs all 'rson' files loaded by the SQVM ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); + sq_showscriptloading = IConVar_RegisterConVar("sq_showscriptloading", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Logs all scripts loaded by the SQVM to be pre-compiled ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); + sq_showvmoutput = IConVar_RegisterConVar("sq_showvmoutput", "3", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Prints the VM output to the console. 1 = Log to file. 2 = 1 + log to console. 3 = 1 + 2 + log to overhead console. 4 = only log to overhead console.", false, 0.f, false, 0.f, nullptr, nullptr); + sq_showvmwarning = IConVar_RegisterConVar("sq_showvmwarning", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Prints the VM warning output to the console. 1 = Log to file. 2 = 1 + log to console.", false, 0.f, false, 0.f, nullptr, nullptr); + //------------------------------------------------------------------------- + // NETCHANNEL | + net_userandomkey = IConVar_RegisterConVar("net_userandomkey", "1", FCVAR_RELEASE, "If set to 1, the netchannel generates and sets a random base64 netkey.", false, 0.f, false, 0.f, nullptr, nullptr); + r5net_matchmaking_hostname = IConVar_RegisterConVar("r5net_matchmaking_hostname", "r5a-comp-sv.herokuapp.com", FCVAR_RELEASE, "Holds the R5Net matchmaking hostname.", false, 0.f, false, 0.f, nullptr, nullptr); + r5net_show_debug = IConVar_RegisterConVar("r5net_show_debug", "1", FCVAR_RELEASE, "Shows debug output for R5Net.", false, 0.f, false, 0.f, nullptr, nullptr); +} + +//----------------------------------------------------------------------------- +// Purpose: clear all hostname ConVar's +//----------------------------------------------------------------------------- +void IConVar_ClearHostNames() +{ + const char* szHostnameArray[] = + { + "pin_telemetry_hostname", + "assetdownloads_hostname", + "users_hostname", + "persistence_hostname", + "speechtotexttoken_hostname", + "communities_hostname", + "persistenceDef_hostname", + "party_hostname", + "speechtotext_hostname", + "serverReports_hostname", + "subscription_hostname", + "steamlink_hostname", + "staticfile_hostname", + "matchmaking_hostname", + "skill_hostname", + "publication_hostname", + "stats_hostname" + }; + + for (int i = 0; i < 17; i++) + { + const char* szName = szHostnameArray[i]; + g_pCvar->FindVar(szName)->m_pzsCurrentValue = "0.0.0.0"; + } +} + +/////////////////////////////////////////////////////////////////////////////// +void IConVar_Attach() +{ + DetourAttach((LPVOID*)&IConVar_IsFlagSet, &HIConVar_IsFlagSet); +} + +void IConVar_Detach() +{ + DetourDetach((LPVOID*)&IConVar_IsFlagSet, &HIConVar_IsFlagSet); +} diff --git a/r5dev/tier0/IConVar.h b/r5dev/tier0/IConVar.h new file mode 100644 index 00000000..bc64759f --- /dev/null +++ b/r5dev/tier0/IConVar.h @@ -0,0 +1,173 @@ +#pragma once +#include "basetypes.h" +#include "ConCommand.h" + +//----------------------------------------------------------------------------- +// Command to ConVars and ConCommands +//----------------------------------------------------------------------------- +// ConVar systems +#define FCVAR_NONE 0 // The default, no flags at all +#define FCVAR_UNREGISTERED (1<<0) // If this is set, don't add to linked list, etc. +#define FCVAR_DEVELOPMENTONLY (1<<1) // Hidden in released products. Flag is removed automatically if ALLOW_DEVELOPMENT_CVARS is defined. +#define FCVAR_GAMEDLL (1<<2) // defined by the game DLL +#define FCVAR_CLIENTDLL (1<<3) // defined by the client DLL +#define FCVAR_HIDDEN (1<<4) // Hidden. Doesn't appear in find or auto complete. Like DEVELOPMENTONLY, but can't be compiled out. + +// ConVar only +#define FCVAR_PROTECTED (1<<5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value +#define FCVAR_SPONLY (1<<6) // This cvar cannot be changed by clients connected to a multiplayer server. +#define FCVAR_ARCHIVE (1<<7) // set to cause it to be saved to vars.rc +#define FCVAR_NOTIFY (1<<8) // notifies players when changed +#define FCVAR_USERINFO (1<<9) // changes the client's info string + +#define FCVAR_PRINTABLEONLY (1<<10) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ). + +#define FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS (1<<10) // When on concommands this allows remote clients to execute this cmd on the server. + // We are changing the default behavior of concommands to disallow execution by remote clients without + // this flag due to the number existing concommands that can lag or crash the server when clients abuse them. + +#define FCVAR_UNLOGGED (1<<11) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log +#define FCVAR_NEVER_AS_STRING (1<<12) // never try to print that cvar + +// It's a ConVar that's shared between the client and the server. +// At signon, the values of all such ConVars are sent from the server to the client (skipped for local +// client, of course ) +// If a change is requested it must come from the console (i.e., no remote client changes) +// If a value is changed while a server is active, it's replicated to all connected clients +#define FCVAR_REPLICATED (1<<13) // server setting enforced on clients, TODO rename to FCAR_SERVER at some time +#define FCVAR_CHEAT (1<<14) // Only useable in singleplayer / debug / multiplayer & sv_cheats +#define FCVAR_SS (1<<15) // causes varnameN where N == 2 through max splitscreen slots for mod to be autogenerated +#define FCVAR_DEMO (1<<16) // record this cvar when starting a demo file +#define FCVAR_DONTRECORD (1<<17) // don't record these command in demofiles +#define FCVAR_SS_ADDED (1<<18) // This is one of the "added" FCVAR_SS variables for the splitscreen players +#define FCVAR_RELEASE (1<<19) // Cvars tagged with this are the only cvars avaliable to customers +#define FCVAR_RELOAD_MATERIALS (1<<20) // If this cvar changes, it forces a material reload +#define FCVAR_RELOAD_TEXTURES (1<<21) // If this cvar changes, if forces a texture reload + +#define FCVAR_NOT_CONNECTED (1<<22) // cvar cannot be changed by a client that is connected to a server +#define FCVAR_MATERIAL_SYSTEM_THREAD (1<<23) // Indicates this cvar is read from the material system thread +#define FCVAR_ARCHIVE_GAMECONSOLE (1<<24) // cvar written to config.cfg on the Xbox + +#define FCVAR_SERVER_CAN_EXECUTE (1<<28) // the server is allowed to execute this command on clients via ClientCommand/NET_StringCmd/CBaseClientState::ProcessStringCmd. +#define FCVAR_SERVER_CANNOT_QUERY (1<<29) // If this is set, then the server is not allowed to query this cvar's value (via IServerPluginHelpers::StartQueryCvarValue). +#define FCVAR_CLIENTCMD_CAN_EXECUTE (1<<30) // IVEngineClient::ClientCmd is allowed to execute this command. + +class ConVar +{ +public: + ConCommandBase m_ConCommandBase; //0x0000 + void* m_pConVarVTable; //0x0040 + ConVar* m_pParent; //0x0048 + const char* n_pszDefaultValue; //0x0050 + const char* m_pzsCurrentValue; //0x0058 + std::int64_t m_iStringLength; //0x0060 + float m_flValue; //0x0068 + int m_iValue; //0x006C + bool m_bHasMin; //0x0070 + float m_flMinValue; //0x0074 + bool m_bHasMax; //0x0078 + float m_flMaxValue; //0x007C + char pad_0080[32]; //0x0080 +}; //Size: 0x00A0 + +class CCVarIteratorInternal // Fully reversed table, just look at the virtual function table and rename the function. +{ +public: + virtual void SetFirst(void) = 0; //0 + virtual void Next(void) = 0; //1 + virtual bool IsValid(void) = 0; //2 + virtual ConCommandBase* Get(void) = 0; //3 +}; + +class CCVar +{ +public: + ConCommandBase* FindCommandBase(const char* szCommandName) // @0x1405983A0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = ConCommandBase * (__thiscall*)(CCVar*, const char*); + return (*reinterpret_cast(this))[14](this, szCommandName); + } + + ConVar* FindVar(const char* szVarName) // @0x1405983B0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = ConVar * (__thiscall*)(CCVar*, const char*); + return (*reinterpret_cast(this))[16](this, szVarName); + } + + void* /*Implement ConCommand class.*/ FindCommand(const char* szCommandName) // @0x1405983F0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void* (__thiscall*)(CCVar*, const char*); + return (*reinterpret_cast(this))[18](this, szCommandName); + } + + CCVarIteratorInternal* FactoryInternalIterator() // @0x140597C10 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = CCVarIteratorInternal * (__thiscall*)(CCVar*); + return (*reinterpret_cast(this))[41](this); + } + + std::unordered_map DumpToMap() + { + std::stringstream ss; + CCVarIteratorInternal* itint = FactoryInternalIterator(); // Allocatd new InternalIterator. + + std::unordered_map allConVars; + + for (itint->SetFirst(); itint->IsValid(); itint->Next()) // Loop through all instances. + { + ConCommandBase* pCommand = itint->Get(); + const char* commandName = pCommand->m_pszName; + allConVars[commandName] = pCommand; + } + + return allConVars; + } +}; + +namespace +{ + /* ==== ICONVAR ========================================================================================================================================================= */ + ADDRESS p_IConVar_IsFlagSet = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\x41\x48\x85\x50\x38", "xxxxxxx"); + bool (*IConVar_IsFlagSet)(ConVar* pConVar, int nFlag) = (bool (*)(ConVar*, int))p_IConVar_IsFlagSet.GetPtr(); /*48 8B 41 48 85 50 38*/ + + ADDRESS p_ConVar_SetInfo = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x60\x48\x8B\xD9\xC6\x41\x10\x00\x33\xC9\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x4C\x24\x00\x0F\x57\xC0\x48\x89\x4C\x24\x00\x48\x89\x03\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x43\x40", "xxxxxxxxxxxxxxxxxx????xxxx?xxxxxxx?xxxxxx????xxxx"); + void* (*ConVar_SetInfo)(void* a1, int a2, int a3, int a4, void* a5) = (void* (*)(void* a1, int a2, int a3, int a4, void* a5))p_ConVar_SetInfo.GetPtr(); /*40 53 48 83 EC 60 48 8B D9 C6 41 10 00 33 C9 48 8D 05 ? ? ? ? 48 89 4C 24 ? 0F 57 C0 48 89 4C 24 ? 48 89 03 48 8D 05 ? ? ? ? 48 89 43 40*/ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_ConVar_Register = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x6C\x24\x00\x48\x89\x74\x24\x00\x48\x89\x7C\x24\x00\x41\x56\x48\x83\xEC\x30\xF3\x0F\x10\x44\x24\x00", "xxxx?xxxx?xxxx?xxxx?xxxxxxxxxxx?"); /*48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC 30 F3 0F 10 44 24 ?*/ + void* (*ConVar_Register)(ConVar* allocatedConvar, const char* szName, const char* szDefaultValue, int nFlags, const char* szHelpString, bool bMin, float fMin, bool bMax, float fMax, void* pCallback, void* unk) = (void* (*)(ConVar*, const char*, const char*, int, const char*, bool, float, bool, float, void*, void*))p_ConVar_Register.GetPtr(); +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_ConVar_Register = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x00\x48\x89\x6C\x24\x00\x48\x89\x74\x24\x00\x57\x48\x83\xEC\x40\xF3\x0F\x10\x84\x24\x00\x00\x00\x00", "xxxx?xxxx?xxxx?xxxxxxxxxx????"); /*48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC 40 F3 0F 10 84 24 ? ? ? ?*/ + void* (*ConVar_Register)(ConVar* allocatedConvar, const char* szName, const char* szDefaultValue, int nFlags, const char* szHelpString, bool bMin, float fMin, bool bMax, float fMax, void* pCallback, void* unk) = (void* (*)(ConVar*, const char*, const char*, int, const char*, bool, float, bool, float, void*, void*))p_ConVar_Register.GetPtr(); +#endif +} + +namespace +{ + ADDRESS g_pConVarVtable = p_ConVar_SetInfo.Offset(0x00).FindPatternSelf("48 8D 05", ADDRESS::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(); // Get vtable ptr for ConVar table. + ADDRESS g_pIConVarVtable = p_ConVar_SetInfo.Offset(0x16).FindPatternSelf("48 8D 05", ADDRESS::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(); // Get vtable ptr for ICvar table. +} + +/////////////////////////////////////////////////////////////////////////////// +bool HIConVar_IsFlagSet(ConVar* pConVar, int nFlag); +void IConVar_InitConVar(); +void IConVar_ClearHostNames(); + +void IConVar_Attach(); +void IConVar_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HConVar : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: IConVar::IsFlagSet : 0x" << std::hex << std::uppercase << p_IConVar_IsFlagSet.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: IConVar::SetInfo : 0x" << std::hex << std::uppercase << p_ConVar_SetInfo.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: IConVar::Register : 0x" << std::hex << std::uppercase << p_ConVar_Register.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pConVarVtable : 0x" << std::hex << std::uppercase << g_pConVarVtable.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pIConVarVtable : 0x" << std::hex << std::uppercase << g_pIConVarVtable.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HConVar); diff --git a/r5dev/tier0/basetypes.h b/r5dev/tier0/basetypes.h new file mode 100644 index 00000000..bc9effff --- /dev/null +++ b/r5dev/tier0/basetypes.h @@ -0,0 +1,19 @@ +#pragma once + +/*----------------------------------------------------------------------------- + * _basetypes + *-----------------------------------------------------------------------------*/ + +//#define GAMEDLL_S0 /*[r]*/ +//#define GAMEDLL_S1 /*[r]*/ +//#define GAMEDLL_S2 /*[i]*/ +#define GAMEDLL_S3 /*[r]*/ +//#define GAMEDLL_S4 /*[i]*/ +//#define GAMEDLL_S7 /*[i]*/ + +#define MAX_SPLITSCREEN_CLIENT_BITS 2 // Max 2 player splitscreen in portal (don't merge this back), saves a bunch of memory [8/31/2010 tom] +#define MAX_SPLITSCREEN_CLIENTS ( 1 << MAX_SPLITSCREEN_CLIENT_BITS ) // 4 // this should == MAX_JOYSTICKS in InputEnums.h + +#define MAX_PLAYERS 128 // Max R5 players. + +#define SDK_VERSION "beta 1.6" diff --git a/r5dev/tier0/completion.cpp b/r5dev/tier0/completion.cpp new file mode 100644 index 00000000..0c47c010 --- /dev/null +++ b/r5dev/tier0/completion.cpp @@ -0,0 +1,588 @@ +//=============================================================================// +// +// Purpose: Completion functions for ConCommand callbacks +// +//=============================================================================// + +#include "core/stdafx.h" +#include "windows/id3dx.h" +#include "tier0/basetypes.h" +#include "tier0/cvar.h" +#include "tier0/IConVar.h" +#include "tier0/completion.h" +#include "engine/net_chan.h" +#include "engine/sys_utils.h" +#include "rtech/rtech.h" +#include "vpklib/packedstore.h" +#include "gameui/IConsole.h" +#include "public/include/bansystem.h" + +#ifndef DEDICATED +void _CGameConsole_f_CompletionFunc(const CCommand& cmd) +{ + g_bShowConsole = !g_bShowConsole; +} + +void _CCompanion_f_CompletionFunc(const CCommand& cmd) +{ + g_bShowBrowser = !g_bShowBrowser; +} +#endif // !DEDICATED + +void _Kick_f_CompletionFunc(CCommand* cmd) +{ + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + const char* firstArg = args[1]; // Get first arg. + + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. + { + CClient* client = g_pClient->GetClientInstance(i); // Get client instance. + if (!client) + { + continue; + } + + if (!client->GetNetChan()) // Netchan valid? + { + continue; + } + + void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. + std::string clientName((char*)clientNamePtr, 32); // Get full name. + + if (clientName.empty()) // Empty name? + { + continue; + } + + if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? + { + continue; + } + + NET_DisconnectClient(client, i, "Kicked from Server", 0, 1); // Disconnect client. + } +} + +void _KickID_f_CompletionFunc(CCommand* cmd) +{ + static auto HasOnlyDigits = [](const std::string& string) + { + for (const char& character : string) + { + if (std::isdigit(character) == 0) + { + return false; + } + } + return true; + }; + + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + std::string firstArg = args[1]; // Get first arg. + + try + { + bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. + { + CClient* client = g_pClient->GetClientInstance(i); // Get client instance. + if (!client) + { + continue; + } + + if (!client->GetNetChan()) // Netchan valid? + { + continue; + } + + std::string finalIpAddress = "null"; // If this stays null they modified the packet somehow. + ADDRESS ipAddressField = ADDRESS(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. + if (ipAddressField) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue()); + + finalIpAddress = ss.str(); + } + + if (onlyDigits) + { + std::int64_t ID = static_cast(std::stoll(firstArg)); + if (ID > MAX_PLAYERS) // Is it a possible originID? + { + std::int64_t originID = client->m_iOriginID; + if (originID != ID) // See if they match. + { + continue; + } + } + else // If its not try by userID. + { + std::int64_t clientID = static_cast(client->m_iUserID + 1); // Get UserID + 1. + if (clientID != ID) // See if they match. + { + continue; + } + } + + NET_DisconnectClient(client, i, "Kicked from Server", 0, 1); // Disconnect client. + } + else + { + if (firstArg.compare(finalIpAddress) != NULL) // Do the string equal? + { + continue; + } + + NET_DisconnectClient(client, i, "Kicked from Server", 0, 1); // Disconnect client. + } + } + } + catch (std::exception& e) + { + DevMsg(eDLL_T::SERVER, "sv_kickid requires a UserID or OriginID. You can get the UserID with the 'status' command. Error: %s", e.what()); + return; + } +} + +void _Ban_f_CompletionFunc(CCommand* cmd) +{ + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + const char* firstArg = args[1]; // Get first arg. + + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. + { + CClient* client = g_pClient->GetClientInstance(i); // Get client instance. + if (!client) + { + continue; + } + + if (!client->GetNetChan()) // Netchan valid? + { + continue; + } + + void* clientNamePtr = (void**)(((std::uintptr_t)client->GetNetChan()) + 0x1A8D); // Get client name from netchan. + std::string clientName((char*)clientNamePtr, 32); // Get full name. + + if (clientName.empty()) // Empty name? + { + continue; + } + + if (strcmp(firstArg, clientName.c_str()) != 0) // Our wanted name? + { + continue; + } + + std::string finalIpAddress = "null"; // If this stays null they modified the packet somehow. + ADDRESS ipAddressField = ADDRESS(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. + if (ipAddressField && ipAddressField.GetValue() != 0x0) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue()); + + finalIpAddress = ss.str(); + } + + g_pBanSystem->AddEntry(finalIpAddress, client->m_iOriginID); // Add ban entry. + g_pBanSystem->Save(); // Save ban list. + NET_DisconnectClient(client, i, "Banned from Server", 0, 1); // Disconnect client. + } +} + +void _BanID_f_CompletionFunc(CCommand* cmd) +{ + static auto HasOnlyDigits = [](const std::string& string) + { + for (const char& character : string) + { + if (std::isdigit(character) == 0) + { + return false; + } + } + return true; + }; + + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + std::string firstArg = args[1]; + + try + { + bool onlyDigits = HasOnlyDigits(firstArg); // Only has digits? + for (int i = 0; i < MAX_PLAYERS; i++) // Loop through all possible client instances. + { + CClient* client = g_pClient->GetClientInstance(i); // Get client instance. + if (!client) + { + continue; + } + + if (!client->GetNetChan()) // Netchan valid? + { + continue; + } + + std::string finalIpAddress = "null"; // If this stays null they modified the packet somehow. + ADDRESS ipAddressField = ADDRESS(((std::uintptr_t)client->GetNetChan()) + 0x1AC0); // Get client ip from netchan. + if (ipAddressField) + { + std::stringstream ss; + ss << std::to_string(ipAddressField.GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x1).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x2).GetValue()) << "." + << std::to_string(ipAddressField.Offset(0x3).GetValue()); + + finalIpAddress = ss.str(); + } + + if (onlyDigits) + { + std::int64_t ID = static_cast(std::stoll(firstArg)); + if (ID > MAX_PLAYERS) // Is it a possible originID? + { + std::int64_t originID = client->m_iOriginID; + if (originID != ID) // See if they match. + { + continue; + } + } + else // If its not try by userID. + { + std::int64_t clientID = static_cast(client->m_iUserID + 1); // Get UserID + 1. + if (clientID != ID) // See if they match. + { + continue; + } + } + + g_pBanSystem->AddEntry(finalIpAddress, client->m_iOriginID); // Add ban entry. + g_pBanSystem->Save(); // Save ban list. + NET_DisconnectClient(client, i, "Banned from Server", 0, 1); // Disconnect client. + } + else + { + if (firstArg.compare(finalIpAddress) != NULL) // Do the string equal? + { + continue; + } + + g_pBanSystem->AddEntry(finalIpAddress, client->m_iOriginID); // Add ban entry. + g_pBanSystem->Save(); // Save ban list. + NET_DisconnectClient(client, i, "Banned from Server", 0, 1); // Disconnect client. + } + } + } + catch (std::exception& e) + { + DevMsg(eDLL_T::SERVER, "Banid Error: %s", e.what()); + return; + } +} + +void _Unban_f_CompletionFunc(CCommand* cmd) +{ + static auto HasOnlyDigits = [](const std::string& string) + { + for (const char& character : string) + { + if (std::isdigit(character) == 0) + { + return false; + } + } + return true; + }; + + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + + try + { + const char* firstArg = args[1]; + if (HasOnlyDigits(firstArg)) // Check if we have an ip address or origin ID. + { + g_pBanSystem->DeleteEntry("noIP", std::stoll(firstArg)); // Delete ban entry. + g_pBanSystem->Save(); // Save modified vector to file. + } + else + { + g_pBanSystem->DeleteEntry(firstArg, 1); // Delete ban entry. + g_pBanSystem->Save(); // Save modified vector to file. + } + } + catch (std::exception& e) + { + DevMsg(eDLL_T::SERVER, "Unban Error: %s", e.what()); + return; + } +} + +void _ReloadBanList_f_CompletionFunc(CCommand* cmd) +{ + g_pBanSystem->Load(); // Reload banlist. +} + +void _RTech_GenerateGUID_f_CompletionFunc(CCommand* cmd) +{ + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + const char* firstArg = args[1]; // Get first arg. + unsigned long long guid = g_pRtech->StringToGuid(firstArg); + + DevMsg(eDLL_T::RTECH, "______________________________________________________________\n"); + DevMsg(eDLL_T::RTECH, "] RTECH_HASH -------------------------------------------------\n"); + DevMsg(eDLL_T::RTECH, "] GUID: '0x%llX'\n", guid); +} + +void _RTech_Decompress_f_CompletionFunc(CCommand* cmd) +{ + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + std::string firstArg = args[1]; // Get first arg. + std::string secondArg = args[2]; // Get second arg. + + const std::string mod_dir = "paks\\Win32\\"; + const std::string base_dir = "paks\\Win64\\"; + + std::string pak_name_out = mod_dir + firstArg + ".rpak"; + std::string pak_name_in = base_dir + firstArg + ".rpak"; + + DevMsg(eDLL_T::RTECH, "______________________________________________________________\n"); + DevMsg(eDLL_T::RTECH, "] RTECH_DECOMPRESS -------------------------------------------\n"); + + if (!FileExists(pak_name_in.c_str())) + { + DevMsg(eDLL_T::RTECH, "Error: pak file '%s' does not exist!\n", pak_name_in.c_str()); + return; + } + + DevMsg(eDLL_T::RTECH, "] Processing: '%s'\n", pak_name_in.c_str()); + + std::vector upak; // Compressed region. + std::ifstream ipak(pak_name_in, std::fstream::binary); + + ipak.seekg(0, std::fstream::end); + upak.resize(ipak.tellg()); + ipak.seekg(0, std::fstream::beg); + ipak.read((char*)upak.data(), upak.size()); + + rpak_h* rheader = (rpak_h*)upak.data(); + uint16_t flags = (rheader->m_nFlags[0] << 8) | rheader->m_nFlags[1]; + + DevMsg(eDLL_T::RTECH, "______________________________________________________________\n"); + DevMsg(eDLL_T::RTECH, "] HEADER_DETAILS ---------------------------------------------\n"); + DevMsg(eDLL_T::RTECH, "] Magic : '%08X'\n", rheader->m_nMagic); + DevMsg(eDLL_T::RTECH, "] Version : '%u'\n", (rheader->m_nVersion)); + DevMsg(eDLL_T::RTECH, "] Flags : '%04X'\n", (flags)); + DevMsg(eDLL_T::RTECH, "] Hash : '%llu'\n", rheader->m_nHash); + DevMsg(eDLL_T::RTECH, "] Entries : '%zu'\n", rheader->m_nAssetEntryCount); + DevMsg(eDLL_T::RTECH, "______________________________________________________________\n"); + DevMsg(eDLL_T::RTECH, "] COMPRESSION_DETAILS ----------------------------------------\n"); + DevMsg(eDLL_T::RTECH, "] Size disk: '%lld'\n", rheader->m_nSizeDisk); + DevMsg(eDLL_T::RTECH, "] Size decp: '%lld'\n", rheader->m_nSizeMemory); + DevMsg(eDLL_T::RTECH, "] Ratio : '%.02f'\n", (rheader->m_nSizeDisk * 100.f) / rheader->m_nSizeMemory); + + if (rheader->m_nMagic != 'kaPR') + { + DevMsg(eDLL_T::RTECH, "Error: pak file '%s' has invalid magic!\n", pak_name_in.c_str()); + return; + } + if ((rheader->m_nFlags[1] & 1) != 1) + { + DevMsg(eDLL_T::RTECH, "Error: pak file '%s' already decompressed!\n", pak_name_in.c_str()); + return; + } + if (rheader->m_nSizeDisk != upak.size()) + { + DevMsg(eDLL_T::RTECH, "Error: pak file '%s' decompressed size '%u' doesn't match expected value '%u'!\n", pak_name_in.c_str(), upak.size(), rheader->m_nSizeMemory); + return; + } + + std::int64_t params[18]; + std::uint32_t dsize = g_pRtech->DecompressedSize((std::int64_t)(params), upak.data(), upak.size(), 0, PAK_HEADER_SIZE); + + if (dsize == rheader->m_nSizeDisk) + { + DevMsg(eDLL_T::RTECH, "Error: calculated size: '%zu' expected: '%zu'!\n", dsize, rheader->m_nSizeMemory); + return; + } + else + { + DevMsg(eDLL_T::RTECH, "] Calculated size: '%zu'\n", dsize); + } + + std::vector pakbuf(rheader->m_nSizeMemory, 0); + + params[1] = std::int64_t(pakbuf.data()); + params[3] = -1i64; + + std::uint8_t decomp_result = g_pRtech->Decompress(params, upak.size(), pakbuf.size()); + if (decomp_result != 1) + { + DevMsg(eDLL_T::RTECH, "Error: decompression failed for '%s' return value: '%u'!\n", pak_name_in.c_str(), +decomp_result); + return; + } + + rheader->m_nFlags[1] = 0x0; // Set compressed flag to false for the decompressed pak file + rheader->m_nSizeDisk = rheader->m_nSizeMemory; // Equal compressed size with decompressed + + std::ofstream out_block(pak_name_out, std::fstream::binary); + std::ofstream out_header(pak_name_out, std::fstream::binary); + + out_block.write((char*)pakbuf.data(), params[5]); + out_header.write((char*)rheader, PAK_HEADER_SIZE); + + DevMsg(eDLL_T::RTECH, "] Decompressed rpak to: '%s'\n", pak_name_out.c_str()); + DevMsg(eDLL_T::RTECH, "--------------------------------------------------------------\n"); +} + +void _NET_TraceNetChan_f_CompletionFunc(CCommand* cmd) +{ + static bool bTraceNetChannel = false; + if (!bTraceNetChannel) + { + g_pCvar->FindVar("net_usesocketsforloopback")->m_pParent->m_iValue = 1; + DevMsg(eDLL_T::ENGINE, "\n"); + DevMsg(eDLL_T::ENGINE, "+--------------------------------------------------------+\n"); + DevMsg(eDLL_T::ENGINE, "|>>>>>>>>>>>>>| NETCHANNEL TRACE ACTIVATED |<<<<<<<<<<<<<|\n"); + DevMsg(eDLL_T::ENGINE, "+--------------------------------------------------------+\n"); + DevMsg(eDLL_T::ENGINE, "\n"); + + // Begin the detour transaction to hook the the process + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + CNetChan_Trace_Attach(); + // Commit the transaction + if (DetourTransactionCommit() != NO_ERROR) + { + // Failed to hook into the process, terminate + TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE); + } + } + else + { + DevMsg(eDLL_T::ENGINE, "\n"); + DevMsg(eDLL_T::ENGINE, "+--------------------------------------------------------+\n"); + DevMsg(eDLL_T::ENGINE, "|>>>>>>>>>>>>| NETCHANNEL TRACE DEACTIVATED |<<<<<<<<<<<<|\n"); + DevMsg(eDLL_T::ENGINE, "+--------------------------------------------------------+\n"); + DevMsg(eDLL_T::ENGINE, "\n"); + + // Begin the detour transaction to hook the the process + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + CNetChan_Trace_Detach(); + + // Commit the transaction + DetourTransactionCommit(); + } + bTraceNetChannel = !bTraceNetChannel; +} + +void _VPK_Decompress_f_CompletionFunc(CCommand* cmd) +{ + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + CCommand& args = *cmd; // Get reference. + std::string firstArg = args[1]; // Get first arg. + std::string szPathOut = "platform\\vpk"; + + std::chrono::milliseconds msStart = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); + + DevMsg(eDLL_T::FS, "______________________________________________________________\n"); + DevMsg(eDLL_T::FS, "] FS_DECOMPRESS ----------------------------------------------\n"); + DevMsg(eDLL_T::FS, "] Processing: '%s'\n", firstArg.c_str()); + + vpk_dir_h vpk = g_pPackedStore->GetPackDirFile(firstArg); + g_pPackedStore->InitLzParams(); + + std::thread th([&] { g_pPackedStore->UnpackAll(vpk, szPathOut); }); + th.join(); + + std::chrono::milliseconds msEnd = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); + float duration = msEnd.count() - msStart.count(); + + DevMsg(eDLL_T::FS, "______________________________________________________________\n"); + DevMsg(eDLL_T::FS, "] OPERATION_DETAILS ------------------------------------------\n"); + DevMsg(eDLL_T::FS, "] Time elapsed: '%.3f' seconds\n", (duration / 1000)); + DevMsg(eDLL_T::FS, "] Decompressed vpk to: '%s'\n", szPathOut.c_str()); + DevMsg(eDLL_T::FS, "--------------------------------------------------------------\n"); +} + +void _NET_SetKey_f_CompletionFunc(CCommand* cmd) +{ + CCommand& args = *cmd; // Get reference. + std::string firstArg = args[1]; // Get first arg. + + std::int32_t argSize = *(std::int32_t*)((std::uintptr_t)cmd + 0x4); + + if (argSize < 2) // Do we atleast have 2 arguments? + { + return; + } + + HNET_SetKey(firstArg); +} + +void _NET_GenerateKey_f_CompletionFunc(CCommand* cmd) +{ + HNET_GenerateKey(); +} diff --git a/r5dev/tier0/completion.h b/r5dev/tier0/completion.h new file mode 100644 index 00000000..5a01962b --- /dev/null +++ b/r5dev/tier0/completion.h @@ -0,0 +1,49 @@ +#pragma once +#include "IConVar.h" + +namespace +{ + /* ==== CONCOMMANDCALLBACK ============================================================================================================================================== */ +#if defined (GAMEDLL_S1) + ADDRESS p_Map_Callback = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x18\x55\x41\x56\x41\x00\x00\x00\x00\x40\x02", "xxxxxxxxx????xx"); + void (*Map_Callback)(CCommand* cmd, char a2) = (void (*)(CCommand*, char))p_Map_Callback.GetPtr(); /*48 89 5C 24 18 55 41 56 41 ?? ?? ?? ?? 40 02*/ +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_Map_Callback = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x55\x41\x56\x41\x57\x48\x81\xEC\x00\x00\x00\x00\x83\x3D", "xxxxxxxxx????xx"); + void (*Map_Callback)(CCommand* cmd, char a2) = (void (*)(CCommand*, char))p_Map_Callback.GetPtr(); /*40 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 83 3D*/ +#endif + ADDRESS p_DownloadPlaylists_Callback = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x33\xC9\xC6\x05\x00\x00\x00\x00\x00\xE9\x00\x00\x00\x00", "xxxx?????x????"); + void (*DownloadPlaylists_Callback)() = (void(*)())p_DownloadPlaylists_Callback.GetPtr(); /*33 C9 C6 05 ?? ?? ?? ?? ?? E9 ?? ?? ?? ??*/ +} + +/////////////////////////////////////////////////////////////////////////////// +#ifndef DEDICATED +void _CGameConsole_f_CompletionFunc(const CCommand& cmd); +void _CCompanion_f_CompletionFunc(const CCommand& cmd); +#endif // !DEDICATED +void _Kick_f_CompletionFunc(CCommand* cmd); +void _KickID_f_CompletionFunc(CCommand* cmd); +void _Ban_f_CompletionFunc(CCommand* cmd); +void _BanID_f_CompletionFunc(CCommand* cmd); +void _Unban_f_CompletionFunc(CCommand* cmd); +void _ReloadBanList_f_CompletionFunc(CCommand* cmd); +void _RTech_GenerateGUID_f_CompletionFunc(CCommand* cmd); +void _RTech_Decompress_f_CompletionFunc(CCommand* cmd); +void _VPK_Decompress_f_CompletionFunc(CCommand* cmd); +void _NET_TraceNetChan_f_CompletionFunc(CCommand* cmd); +void _NET_SetKey_f_CompletionFunc(CCommand* cmd); +void _NET_GenerateKey_f_CompletionFunc(CCommand* cmd); + + +/////////////////////////////////////////////////////////////////////////////// +class HCompletion : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: Map_Callback : 0x" << std::hex << std::uppercase << p_Map_Callback.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: DownloadPlaylists_Callback : 0x" << std::hex << std::uppercase << p_DownloadPlaylists_Callback.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HCompletion); diff --git a/r5dev/tier0/cvar.cpp b/r5dev/tier0/cvar.cpp new file mode 100644 index 00000000..af17f00c --- /dev/null +++ b/r5dev/tier0/cvar.cpp @@ -0,0 +1,40 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "tier0/IConVar.h" +#include "engine/sys_dll2.h" + +//------------------------------------------------------------------------- +// ENGINE | +ConVar* cm_debug_cmdquery = new ConVar(); +ConVar* cm_return_false_cmdquery_all = new ConVar(); +ConVar* cm_return_false_cmdquery_dev_cheat = new ConVar(); +//------------------------------------------------------------------------- +// SERVER | +ConVar* sv_showconnecting = new ConVar(); +//------------------------------------------------------------------------- +// CLIENT | +ConVar* cl_drawconsoleoverlay = new ConVar(); +ConVar* cl_consoleoverlay_lines = new ConVar(); +ConVar* cl_consoleoverlay_offset_x = new ConVar(); +ConVar* cl_consoleoverlay_offset_y = new ConVar(); +//------------------------------------------------------------------------- +// FILESYSTEM | +ConVar* fs_warning_level_native = new ConVar(); +ConVar* fs_packedstore_entryblock_stats = new ConVar(); +//------------------------------------------------------------------------- +// MATERIALSYSTEM | +ConVar* mat_showdxoutput = new ConVar(); +//------------------------------------------------------------------------- +// SQUIRREL | +ConVar* sq_showrsonloading = new ConVar(); +ConVar* sq_showscriptloading = new ConVar(); +ConVar* sq_showvmoutput = new ConVar(); +ConVar* sq_showvmwarning = new ConVar(); +//------------------------------------------------------------------------- +// NETCHANNEL | +ConVar* net_userandomkey = new ConVar(); +ConVar* r5net_matchmaking_hostname = new ConVar(); +ConVar* r5net_show_debug = new ConVar(); + +/////////////////////////////////////////////////////////////////////////////// +CCVar* g_pCvar = reinterpret_cast(p_CEngineAPI_Connect.FindPatternSelf("48 8D 0D", ADDRESS::Direction::DOWN, 40).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr()); diff --git a/r5dev/tier0/cvar.h b/r5dev/tier0/cvar.h new file mode 100644 index 00000000..022f1eae --- /dev/null +++ b/r5dev/tier0/cvar.h @@ -0,0 +1,66 @@ +#pragma once +#include "tier0/basetypes.h" +#include "tier0/IConVar.h" + +namespace +{ + /* ==== CCVAR =========================================================================================================================================================== */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_CCvar_Disconnect = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x57\x41\x56\x48\x83\xEC\x38\x4C\x8B\x35", "xxxxxxxxxxx"); + void* (*CCvar_Disconnect)() = (void* (*)())p_CCvar_Disconnect.GetPtr(); /*40 57 41 56 48 83 EC 38 4C 8B 35 ? ? ? ?*/ +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_CCvar_Disconnect = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x48\x8B\x0D\x00\x00\x00\x00\x48\x85\xC9\x74\x26\x80\x3D\x00\x00\x00\x00\x00\x74\x1D\x48\x8B\x01\x8B\x15\x00\x00\x00\x00\xFF\x50\x58\xC7\x05\x00\x00\x00\x00\x00\x00\x00\x00\xC6\x05\x00\x00\x00\x00\x00\x48\xC7\x05\x00\x00\x00", "xxxxxxx????xxxxxxx?????xxxxxxx????xxxxx????????xx"); + void* (*CCvar_Disconnect)() = (void* (*)())p_CCvar_Disconnect.GetPtr(); /*48 83 EC 28 48 8B 0D ? ? ? ? 48 85 C9 74 26 80 3D ? ? ? ? ? 74 1D 48 8B 01 8B 15 ? ? ? ? FF 50 58 C7 05 ? ? ? ? ? ? ? ? C6 05 ? ? ? ? ? 48 C7 05 ? ? ? ? ? ? ? ?*/ +#endif +} + +//------------------------------------------------------------------------- +// ENGINE | +extern ConVar* cm_debug_cmdquery; +extern ConVar* cm_return_false_cmdquery_all; +extern ConVar* cm_return_false_cmdquery_dev_cheat; +//------------------------------------------------------------------------- +// SERVER | +extern ConVar* sv_showconnecting; +//------------------------------------------------------------------------- +// CLIENT | +extern ConVar* cl_drawconsoleoverlay; +extern ConVar* cl_consoleoverlay_lines; +extern ConVar* cl_consoleoverlay_offset_x; +extern ConVar* cl_consoleoverlay_offset_y; +//------------------------------------------------------------------------- +// FILESYSTEM | +extern ConVar* fs_warning_level_native; +extern ConVar* fs_packedstore_entryblock_stats; +//------------------------------------------------------------------------- +// FILESYSTEM | +extern ConVar* mat_showdxoutput; +//------------------------------------------------------------------------- +// SQUIRREL | +extern ConVar* sq_showrsonloading; +extern ConVar* sq_showscriptloading; +extern ConVar* sq_showvmoutput; +extern ConVar* sq_showvmwarning; +//------------------------------------------------------------------------- +// NETCHANNEL | +extern ConVar* net_userandomkey; +extern ConVar* r5net_matchmaking_hostname; +extern ConVar* r5net_show_debug; + +/////////////////////////////////////////////////////////////////////////////// +extern CCVar* g_pCvar; + + +/////////////////////////////////////////////////////////////////////////////// +class HCvar : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CCvar::Disconnect : 0x" << std::hex << std::uppercase << p_CCvar_Disconnect.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pCvar : 0x" << std::hex << std::uppercase << g_pCvar << std::setw(0) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HCvar); diff --git a/r5dev/tier0/interface.h b/r5dev/tier0/interface.h new file mode 100644 index 00000000..f41e6ef7 --- /dev/null +++ b/r5dev/tier0/interface.h @@ -0,0 +1,4 @@ +#pragma once + +typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode); +typedef void* (*InstantiateInterfaceFn)(); diff --git a/r5dev/vgui/CEngineVGui.cpp b/r5dev/vgui/CEngineVGui.cpp new file mode 100644 index 00000000..ed912a0e --- /dev/null +++ b/r5dev/vgui/CEngineVGui.cpp @@ -0,0 +1,118 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "vgui/CEngineVGui.h" +#include "vguimatsurface/MatSystemSurface.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int HCEngineVGui_Paint(void* thisptr, int mode) +{ + int result = CEngineVGui_Paint(thisptr, mode); + + static void* pCMatSystemSurface = ADDRESS(0x14D40B3B0).RCast(); + static auto fnRenderStart = ADDRESS(0x14053EFC0).RCast(); + static auto fnRenderEnd = ADDRESS(0x14053F1B0).RCast(); + + if (mode == 1 || mode == 2) // Render in-main menu and in-game. + { + fnRenderStart(pCMatSystemSurface); + + g_pLogSystem.Update(); + + fnRenderEnd(); + } + + return result; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CLogSystem::Update() +{ + if (cl_drawconsoleoverlay->m_pParent->m_iValue < 1) + { + return; + } + if (m_vLogs.empty()) + { + return; + } + if (!g_pMatSystemSurface) + { + return; + } + + static int fontHeight = 16; + + for (int i = 0; i < m_vLogs.size(); ++i) + { + if (m_vLogs[i].Ticks >= 0) + { + if (i < cl_consoleoverlay_lines->m_pParent->m_iValue) + { + float fadepct = fminf(static_cast(m_vLogs[i].Ticks) / 255.f, 4.0); // TODO [ AMOS ]: register a ConVar for this! + float ptc = static_cast(ceilf(fadepct * 100.f)); // TODO [ AMOS ]: register a ConVar for this! + int alpha = static_cast(ptc); + int y = (cl_consoleoverlay_offset_y->m_pParent->m_iValue + (fontHeight * i)); + int x = cl_consoleoverlay_offset_x->m_pParent->m_iValue; + + std::array color = GetLogColorForType(m_vLogs[i].Type); + CMatSystemSurface_DrawColoredText(g_pMatSystemSurface, 0x13, fontHeight, x, y, color[0], color[1], color[2], alpha, m_vLogs[i].Message.c_str()); + } + else + { + m_vLogs.erase(m_vLogs.begin()); + continue; + } + + m_vLogs[i].Ticks--; + } + else + { + m_vLogs.erase(m_vLogs.begin() + i); + } + } +} + +void CLogSystem::AddLog(LogType_t type, std::string message) +{ + if (message.length() > 0) + { + m_vLogs.push_back(Log{ message, 1024, type }); + } +} + +std::array CLogSystem::GetLogColorForType(LogType_t type) +{ + switch (type) + { + case LogType_t::NATIVE: + return { 255, 255, 255 }; + case LogType_t::SCRIPT_SERVER: + return { 190, 183, 240 }; + case LogType_t::SCRIPT_CLIENT: + return { 117, 116, 139 }; + case LogType_t::SCRIPT_UI: + return { 197, 160, 177 }; + default: + return { 255, 255, 255 }; + } + + return { 255, 255, 255 }; +} + +/////////////////////////////////////////////////////////////////////////////// +void CEngineVGui_Attach() +{ + //DetourAttach((LPVOID*)&CEngineVGui_Paint, &HCEngineVGui_Paint); +} + +void CEngineVGui_Detach() +{ + //DetourDetach((LPVOID*)&CEngineVGui_Paint, &HCEngineVGui_Paint); +} + +/////////////////////////////////////////////////////////////////////////////// +CLogSystem g_pLogSystem; diff --git a/r5dev/vgui/CEngineVGui.h b/r5dev/vgui/CEngineVGui.h new file mode 100644 index 00000000..fbdcd77b --- /dev/null +++ b/r5dev/vgui/CEngineVGui.h @@ -0,0 +1,76 @@ +#pragma once +#include "core/stdafx.h" +#include "tier0/basetypes.h" + +enum class LogType_t : int +{ + SCRIPT_SERVER, + SCRIPT_CLIENT, + SCRIPT_UI, + SCRIPT_WARNING, + NATIVE +}; + +struct Log +{ + Log(const std::string Message, const int Ticks, const LogType_t Type) + { + this->Message = Message; + this->Ticks = Ticks; + this->Type = Type; + } + std::string Message = ""; + int Ticks = 1024; + LogType_t Type = LogType_t::NATIVE; +}; + +class CLogSystem +{ +public: + void AddLog(LogType_t type, std::string text); + void Update(); + +private: + std::array GetLogColorForType(LogType_t type); + std::vector m_vLogs{}; +}; + +namespace +{ + /* ==== CENGINEVGUI ===================================================================================================================================================== */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_CEngineVGui_Paint = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x89\x54\x24\x10\x55\x56\x41\x55\x48\x81\xEC\x00\x00\x00\x00", "xxxxxxxxxxx????"); + int (*CEngineVGui_Paint)(void* thisptr, int mode) = (int (*)(void*, int))p_CEngineVGui_Paint.GetPtr(); /*41 55 41 56 48 83 EC 78 44 8B EA*/ + + ADDRESS p_CEngineVGui_Unknown = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x8B\x81\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x4C\x3B\xC0\x74\x1F", "xxx????xxx????xxxxx"); + void** (*CEngineVGui_Unknown)(std::int64_t a1) = (void** (*)(std::int64_t))p_CEngineVGui_Paint.GetPtr(); /*4C 8B 81 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 4C 3B C0 74 1F*/ +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_CEngineVGui_Paint = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x41\x55\x41\x56\x48\x83\xEC\x78\x44\x8B\xEA", "xxxxxxxxxxx"); + int (*CEngineVGui_Paint)(void* thisptr, int mode) = (int (*)(void*, int))p_CEngineVGui_Paint.GetPtr(); /*41 55 41 56 48 83 EC 78 44 8B EA*/ + + ADDRESS p_CEngineVGui_Unknown = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x48\x8D\x05\x00\x00\x00\x00\x48\x8B\xD9\x48\x39\x81\x00\x00\x00\x00\x74\x29", "xxxxxxxxx????xxxxxx????xx"); + void** (*CEngineVGui_Unknown)(std::int64_t a1) = (void** (*)(std::int64_t))p_CEngineVGui_Paint.GetPtr(); /*40 53 48 83 EC 20 48 8D 05 ?? ?? ?? ?? 48 8B D9 48 39 81 ?? ?? ?? ?? 74 29*/ +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +int HCEngineVGui_Paint(void* thisptr, int mode); +void CEngineVGui_Attach(); +void CEngineVGui_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +extern CLogSystem g_pLogSystem; + +/////////////////////////////////////////////////////////////////////////////// +class HEngineVGui : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CEngineVGui::Paint : 0x" << std::hex << std::uppercase << p_CEngineVGui_Paint.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: CEngineVGui::Unknown : 0x" << std::hex << std::uppercase << p_CEngineVGui_Unknown.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HEngineVGui); diff --git a/r5dev/vgui/vgui.h b/r5dev/vgui/vgui.h new file mode 100644 index 00000000..dfb22c15 --- /dev/null +++ b/r5dev/vgui/vgui.h @@ -0,0 +1,19 @@ +#pragma once + +namespace vgui +{ + // handle to an internal vgui panel + // this is the only handle to a panel that is valid across dll boundaries + typedef unsigned int VPANEL; + + // handles to vgui objects + // NULL values signify an invalid value + typedef unsigned long HScheme; + // Both -1 and 0 are used for invalid textures. Be careful. + typedef unsigned long HTexture; + typedef unsigned long HCursor; + typedef unsigned long HPanel; + const HPanel INVALID_PANEL = 0xffffffff; + typedef unsigned long HFont; + const HFont INVALID_FONT = 0; // the value of an invalid font handle +} diff --git a/r5dev/vgui/vgui_fpspanel.cpp b/r5dev/vgui/vgui_fpspanel.cpp new file mode 100644 index 00000000..26a0a8fb --- /dev/null +++ b/r5dev/vgui/vgui_fpspanel.cpp @@ -0,0 +1,20 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "vgui/vgui_fpspanel.h" +#include "vgui/CEngineVGui.h" + +ConVar* HCFPSPanel_Paint(void* thisptr) +{ + g_pLogSystem.Update(); + return CFPSPanel_Paint(thisptr); +} + +void CFPSPanel_Attach() +{ + DetourAttach((LPVOID*)&CFPSPanel_Paint, &HCFPSPanel_Paint); +} + +void CFPSPanel_Detach() +{ + DetourDetach((LPVOID*)&CFPSPanel_Paint, &HCFPSPanel_Paint); +} diff --git a/r5dev/vgui/vgui_fpspanel.h b/r5dev/vgui/vgui_fpspanel.h new file mode 100644 index 00000000..5e4693be --- /dev/null +++ b/r5dev/vgui/vgui_fpspanel.h @@ -0,0 +1,24 @@ +#pragma once +#include "tier0/IConVar.h" +namespace +{ + /* ==== CFPSPANEL ======================================================================================================================================================= */ + ADDRESS p_CFPSPanel_Paint = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\xC4\x55\x56\x41\x00\x48\x8D\xA8\x00\xFD\xFF\xFF\x48\x81\xEC\x80", "xxxxxx?xxx?xxxxxxx"); + ConVar* (*CFPSPanel_Paint)(void* thisptr) = (ConVar* (*)(void*))p_CFPSPanel_Paint.GetPtr(); /*48 8B C4 55 56 41 ?? 48 8D A8 ?? FD FF FF 48 81 EC 80*/ +} + +void CFPSPanel_Attach(); +void CFPSPanel_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HFPSPanel : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CFPSPanel::Paint : 0x" << std::hex << std::uppercase << p_CFPSPanel_Paint.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HFPSPanel); diff --git a/r5dev/vguimatsurface/MatSystemSurface.cpp b/r5dev/vguimatsurface/MatSystemSurface.cpp new file mode 100644 index 00000000..0090c60c --- /dev/null +++ b/r5dev/vguimatsurface/MatSystemSurface.cpp @@ -0,0 +1,3 @@ +#include "core/stdafx.h" +#include "vguimatsurface/MatSystemSurface.h" +//TODO diff --git a/r5dev/vguimatsurface/MatSystemSurface.h b/r5dev/vguimatsurface/MatSystemSurface.h new file mode 100644 index 00000000..bb2b9dfa --- /dev/null +++ b/r5dev/vguimatsurface/MatSystemSurface.h @@ -0,0 +1,31 @@ +#pragma once +#include "client/cdll_engine_int.h" +namespace +{ + /* ==== CMATSYSTEMSURFACE =============================================================================================================================================== */ + ADDRESS p_CMatSystemSurface_DrawColoredText = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x8B\xDC\x48\x83\xEC\x68\x49\x8D\x43\x58\x0F\x57\xC0", "xxxxxxxxxxxxxx"); /*4C 8B DC 48 83 EC 68 49 8D 43 58 0F 57 C0*/ + void* (*CMatSystemSurface_DrawColoredText)(void* thisptr, int font, int fontHeight, int offsetX, int offsetY, int red, int green, int blue, int alpha, const char* text, ...) = (void* (*)(void*, int, int, int, int, int, int, int, int, const char*, ...))p_CMatSystemSurface_DrawColoredText.GetPtr(); +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_CMatSystemSurface_Unknown0 = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\x0D\x00\x00\x00\x00\x48\x8B\x01\x48\xFF\xA0\x18\x01\x00\x00", "xxx????xxxxxxxxxx"); /*48 8B 0D ?? ?? ?? ?? 48 8B 01 48 FF A0 18 01 00 00*/ + std::int64_t(*CMatSystemSurface_Unknown0)() = (std::int64_t(*)())p_CMatSystemSurface_Unknown0.GetPtr(); // [ AMOS ] DELETE +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_CMatSystemSurface_Unknown0 = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\x0D\x00\x00\x00\x00\x48\x8B\x01\x48\xFF\xA0\x20\x01\x00\x00", "xxx????xxxxxxxxxx"); /*48 8B 0D ?? ?? ?? ?? 48 8B 01 48 FF A0 20 01 00 00*/ + void*(*CMatSystemSurface_Unknown0)() = (void*(*)())p_CMatSystemSurface_Unknown0.GetPtr(); // [ AMOS ] DELETE +#endif + ADDRESS g_pMatSystemSurface = p_CHLClient_PostInit.FindPatternSelf("48 83 3D", ADDRESS::Direction::DOWN, 40).ResolveRelativeAddressSelf(0x3, 0x8).GetPtr(); +} + +/////////////////////////////////////////////////////////////////////////////// +class HMatSystemSurface : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CMatSystemSurface::DrawColoredText : 0x" << std::hex << std::uppercase << p_CMatSystemSurface_DrawColoredText.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: CMatSystemSurface::Unknown0 : 0x" << std::hex << std::uppercase << p_CMatSystemSurface_Unknown0.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pMatSystemSurface : 0x" << std::hex << std::uppercase << g_pMatSystemSurface.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HMatSystemSurface); diff --git a/r5dev/vpc/IAppSystem.cpp b/r5dev/vpc/IAppSystem.cpp new file mode 100644 index 00000000..5f5abc70 --- /dev/null +++ b/r5dev/vpc/IAppSystem.cpp @@ -0,0 +1,3 @@ +#include "core/stdafx.h" +#include "IAppSystem.h" +//TODO diff --git a/r5dev/vpc/IAppSystem.h b/r5dev/vpc/IAppSystem.h new file mode 100644 index 00000000..1a959faa --- /dev/null +++ b/r5dev/vpc/IAppSystem.h @@ -0,0 +1,22 @@ +#pragma once +#include "tier0/basetypes.h" + +namespace +{ + /* ==== IAPPSYSTEM ============================================================================================================================================== */ + ADDRESS p_IAppSystem_LoadLibrary = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x83\xEC\x28\x48\x8B\x0D\x00\x00\x00\x00\x48\x8D\x05\x00\x00\x00\x00\x48\x89\x05\x00\x00\x00\x00\x48\x85\xC9\x74\x11", "xxxxxxx????xxx????xxx????xxxxx"); + void* (*IAppSystem_LoadLibrary)() = (void * (*)())p_IAppSystem_LoadLibrary.GetPtr(); /*48 83 EC 28 48 8B 0D ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 85 C9 74 11*/ // C initializers/terminators +} + +/////////////////////////////////////////////////////////////////////////////// +class HAppSystem : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: IAppSystem::LoadLibrary : 0x" << std::hex << std::uppercase << p_IAppSystem_LoadLibrary.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HAppSystem); diff --git a/r5dev/vpc/basefilesystem.cpp b/r5dev/vpc/basefilesystem.cpp new file mode 100644 index 00000000..fc127b30 --- /dev/null +++ b/r5dev/vpc/basefilesystem.cpp @@ -0,0 +1,63 @@ +#include "core/stdafx.h" +#include "core/logdef.h" +#include "tier0/cvar.h" +#include "vpc/basefilesystem.h" +#include "gameui/IConsole.h" + +//--------------------------------------------------------------------------------- +// Purpose: prints the output of the filesystem based on the warning level +//--------------------------------------------------------------------------------- +void HCBaseFileSystem_Warning(void* thisptr, FileWarningLevel_t level, const char* fmt, ...) +{ + if (fs_warning_level_native->m_pParent->m_iValue < (int)level) + { + return; + } + + static bool initialized = false; + static char buf[1024] = {}; + + static auto iconsole = spdlog::stdout_logger_mt("fs_warn_iconsole"); // in-game console. + static auto wconsole = spdlog::stdout_logger_mt("fs_warn_wconsole"); // windows console. + + fs_oss.str(""); + fs_oss.clear(); + + if (!initialized) + { + iconsole = std::make_shared("fs_warn_ostream", fs_ostream_sink); + iconsole->set_pattern("[%S.%e] %v"); + iconsole->set_level(spdlog::level::debug); + wconsole->set_pattern("[%S.%e] %v"); + wconsole->set_level(spdlog::level::debug); + initialized = true; + } + + va_list args{}; + va_start(args, fmt); + + vsnprintf(buf, sizeof(buf), fmt, args); + + buf[sizeof(buf) - 1] = 0; + va_end(args); + + iconsole->debug(buf); + wconsole->debug(buf); + +#ifndef DEDICATED + std::string s = fs_oss.str(); + const char* c = s.c_str(); + + Items.push_back(Strdup((const char*)c)); +#endif // !DEDICATED +} + +void CBaseFileSystem_Attach() +{ + DetourAttach((LPVOID*)&CBaseFileSystem_Warning, &HCBaseFileSystem_Warning); +} + +void CBaseFileSystem_Detach() +{ + DetourDetach((LPVOID*)&CBaseFileSystem_Warning, &HCBaseFileSystem_Warning); +} diff --git a/r5dev/vpc/basefilesystem.h b/r5dev/vpc/basefilesystem.h new file mode 100644 index 00000000..cc16fc15 --- /dev/null +++ b/r5dev/vpc/basefilesystem.h @@ -0,0 +1,37 @@ +#pragma once + +enum class FileWarningLevel_t : int +{ + FILESYSTEM_WARNING = -1, // A problem! + FILESYSTEM_WARNING_QUIET = 0, // Don't print anything + FILESYSTEM_WARNING_REPORTUNCLOSED, // On shutdown, report names of files left unclosed + FILESYSTEM_WARNING_REPORTUSAGE, // Report number of times a file was opened, closed + FILESYSTEM_WARNING_REPORTALLACCESSES, // Report all open/close events to console ( !slow! ) + FILESYSTEM_WARNING_REPORTALLACCESSES_READ, // Report all open/close/read events to the console ( !slower! ) + FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE, // Report all open/close/read/write events to the console ( !slower! ) + FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC // Report all open/close/read/write events and all async I/O file events to the console ( !slower(est)! ) +}; + +namespace +{ + /* ==== CBASEFILESYSTEM ================================================================================================================================================= */ + ADDRESS p_CBaseFileSystem_Warning = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x4C\x89\x4C\x24\x20\xC3\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\x48", "xxxxxx??????????x"); + void (*CBaseFileSystem_Warning)(void* thisptr, FileWarningLevel_t level, const char* fmt, ...) = (void (*)(void*, FileWarningLevel_t, const char*, ...))p_CBaseFileSystem_Warning.GetPtr(); /*4C 89 4C 24 20 C3 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 48*/ +} + +/////////////////////////////////////////////////////////////////////////////// +void CBaseFileSystem_Attach(); +void CBaseFileSystem_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HBaseFileSystem : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: CBaseFileSystem::Warning : 0x" << std::hex << std::uppercase << p_CBaseFileSystem_Warning.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HBaseFileSystem); diff --git a/r5dev/vpc/interfaces.h b/r5dev/vpc/interfaces.h new file mode 100644 index 00000000..aef3f6e3 --- /dev/null +++ b/r5dev/vpc/interfaces.h @@ -0,0 +1,15 @@ +#pragma once + +/*----------------------------------------------------------------------------- + * _interfaces.h + *-----------------------------------------------------------------------------*/ + +//----------------------------------------------------------------------------- +// Mapping of interface string to globals +//----------------------------------------------------------------------------- +struct InterfaceGlobals_t +{ + std::int64_t(*m_pInterfacePtr)(void); + const char* m_pInterfaceName; + std::int64_t* m_pNextInterfacePtr; +}; diff --git a/r5dev/vpc/keyvalues.cpp b/r5dev/vpc/keyvalues.cpp new file mode 100644 index 00000000..e9735d1c --- /dev/null +++ b/r5dev/vpc/keyvalues.cpp @@ -0,0 +1,100 @@ +#include "core/stdafx.h" +#include "vpc/keyvalues.h" +#include "rtech/stryder.h" +#include "engine/sys_dll2.h" + +/////////////////////////////////////////////////////////////////////////////// +std::vector g_szAllPlaylists = { "none" }; +CKeyValuesSystem* g_pKeyValuesSystem = reinterpret_cast(p_KeyValues_Init.FindPatternSelf("48 8D ?? ?? ?? ?? 01", ADDRESS::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr()); + +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) +KeyValues** g_pPlaylistKeyValues = reinterpret_cast(p_Stryder_StitchRequest.FindPatternSelf("48 8B 2D", ADDRESS::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr()); // Get the KeyValue for the playlist file. +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) +KeyValues** g_pPlaylistKeyValues = reinterpret_cast(p_Stryder_StitchRequest.FindPatternSelf("48 8B 0D", ADDRESS::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr()); // Get the KeyValue for the playlist file. +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CKeyValueSystem_InitPlaylist() +{ + while (true) + { + if ((*g_pPlaylistKeyValues)) + { + KeyValues* playlists = (*g_pPlaylistKeyValues)->FindKey("Playlists", false); // Find playlists key. + if (playlists) + { + g_szAllPlaylists.clear(); + for (KeyValues* dat = playlists->m_pSub; dat != nullptr; dat = dat->m_pPeer) // Parse through all sub keys. + { + g_szAllPlaylists.push_back(dat->GetName()); // Get all playlist names. + } + + break; // Break if playlist got filled. + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool HKeyValues_LoadPlaylist(const char* playlist) +{ + memset(g_pMapVPKCache, 0, 0x40); // Clear VPK cache to prevent crash while loading playlist. + + CHAR sPlaylistPath[] = "\x77\x27\x35\x2b\x2c\x6c\x2b\x2c\x2b"; + PCHAR curr = sPlaylistPath; + while (*curr) + { + *curr ^= 'B'; + ++curr; + } + + if (FileExists(sPlaylistPath)) + { + std::uint8_t verifyPlaylistIntegrity[] = // Very hacky way for alternative inline assembly for x64.. + { + 0x48, 0x8B, 0x45, 0x58, // mov rcx, playlist + 0xC7, 0x00, 0x00, 0x00, 0x00, // test playlist, playlist + 0x00 + }; + void* verifyPlaylistIntegrityFn = nullptr; + VirtualAlloc(verifyPlaylistIntegrity, 10, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + memcpy(&verifyPlaylistIntegrityFn, (const void*)verifyPlaylistIntegrity, 9); + reinterpret_cast(verifyPlaylistIntegrityFn)(); + } + + return KeyValues_LoadPlaylist(playlist); // Parse playlist. +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char* KeyValues::GetName() +{ + return g_pKeyValuesSystem->GetStringForSymbol(MAKE_3_BYTES_FROM_1_AND_2(m_iKeyNameCaseSensitive, m_iKeyNameCaseSensitive2)); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CKeyValueSystem_Init() +{ + std::thread t1(CKeyValueSystem_InitPlaylist); // Start thread to grab playlists. + t1.detach(); // Detach thread from current one. +} + +/////////////////////////////////////////////////////////////////////////////// +void CKeyValueSystem_Attach() +{ + DetourAttach((LPVOID*)&KeyValues_LoadPlaylist, &HKeyValues_LoadPlaylist); +} + +void CKeyValueSystem_Detach() +{ + DetourDetach((LPVOID*)&KeyValues_LoadPlaylist, &HKeyValues_LoadPlaylist); +} diff --git a/r5dev/vpc/keyvalues.h b/r5dev/vpc/keyvalues.h new file mode 100644 index 00000000..9963479a --- /dev/null +++ b/r5dev/vpc/keyvalues.h @@ -0,0 +1,263 @@ +#pragma once +#include "tier0/basetypes.h" + +#define MAKE_3_BYTES_FROM_1_AND_2( x1, x2 ) (( (( std::uint16_t )x2) << 8 ) | (std::uint8_t)(x1)) +extern std::vector g_szAllPlaylists; +typedef int HKeySymbol; + +enum KeyValuesTypes +{ + TYPE_NONE = 0x0, + TYPE_STRING = 0x1, + TYPE_INT = 0x2, + TYPE_FLOAT = 0x3, + TYPE_PTR = 0x4, + TYPE_WSTRING = 0x5, + TYPE_COLOR = 0x6, + TYPE_UINT64 = 0x7, + TYPE_COMPILED_INT_BYTE = 0x8, + TYPE_COMPILED_INT_0 = 0x9, + TYPE_COMPILED_INT_1 = 0xA, + TYPE_NUMTYPES = 0xB, +}; + +namespace +{ + /* ==== KEYVALUES ======================================================================================================================================================= */ +#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) + ADDRESS p_KeyValues_Init = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x48\x8B\xD9\xC7\x44\x24\x30\xFF\xFF\xFF", "xxxxxxxxxxxxxxxx"); /*40 53 48 83 EC 20 48 8B D9 C7 44 24 30 FF FF FF*/ + std::int64_t(*KeyValues_Init)(std::int64_t a1, std::int64_t a2, std::int64_t a3, std::int64_t a4) = (std::int64_t(*)(std::int64_t, std::int64_t, std::int64_t, std::int64_t))p_KeyValues_Init.GetPtr(); + + ADDRESS p_KeyValues_FindKey = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x5C\x24\x10\x48\x89\x6C\x24\x18\x48\x89\x74\x24\x20\x57\x41\x54\x41\x55\x41\x56\x41\x57\x48\x81\xEC\x20\x01\x00\x00\x45", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + void* (*KeyValues_FindKey)(void* a1, const char* a2, bool a3) = (void* (*)(void*, const char*, bool))p_KeyValues_FindKey.GetPtr(); /*48 89 5C 24 10 48 89 6C 24 18 48 89 74 24 20 57 41 54 41 55 41 56 41 57 48 81 EC 20 01 00 00 45*/ +#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) + ADDRESS p_KeyValues_Init = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x53\x48\x83\xEC\x20\x48\x8B\x05\x00\x00\x00\x01\x48\x8B\xD9\x4C\x8B\xC2", "xxxxxxxxx???xxxxxxx"); /*40 53 48 83 EC 20 48 8B 05 ?? ?? ?? 01 48 8B D9 4C 8B C2*/ + void* (*KeyValues_Init)(std::int64_t a1, std::int64_t a2, std::int64_t a3, std::int64_t a4) = (void* (*)(std::int64_t, std::int64_t, std::int64_t, std::int64_t))p_KeyValues_Init.GetPtr(); + + ADDRESS p_KeyValues_FindKey = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x40\x56\x57\x41\x57\x48\x81\xEC\x00\x00\x00\x00\x45", "xxxxxxxx????x"); + void* (*KeyValues_FindKey)(void* a1, const char* a2, bool a3) = (void* (*)(void*, const char*, bool))p_KeyValues_FindKey.GetPtr(); /*40 56 57 41 57 48 81 EC 30 01 00 00 45 0F B6 F8*/ +#endif + ADDRESS p_KeyValues_LoadPlaylist = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\xE8\x00\x00\x00\x00\x80\x3D\x00\x00\x00\x00\x00\x74\x0C", "x????xx?????xx").FollowNearCallSelf().GetPtr();; + bool (*KeyValues_LoadPlaylist)(const char* source) = (bool (*)(const char*))p_KeyValues_LoadPlaylist.GetPtr(); /*E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 74 0C*/ + + ADDRESS p_KeyValues_GetMemPool = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x8B\x05\x00\x00\x00\x00\xC3\xCC\xCC\xCC\xCC\xCC\xCC\xCC\xCC\x48\x85\xD2", "xxx????xxxxxxxxxxxx"); + void* (*KeyValues_GetMemPool)() = (void* (*)())p_KeyValues_GetMemPool.GetPtr(); /*48 8B 05 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC 48 85 D2*/ + + std::uintptr_t g_pKeyValuesMemPool = p_KeyValues_GetMemPool.ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(); +} + +class CKeyValuesSystem // VTABLE @ 0x1413AA1E8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM +{ +public: + + void RegisterSizeofKeyValues(std::int64_t size) //@0x1413AA1F0 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, std::int64_t); + (*reinterpret_cast(this))[0](this, size); + } + + void* AllocKeyValuesMemory(std::int64_t size) // @0x1413AA1F8 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void* (__thiscall*)(CKeyValuesSystem*, std::int64_t); + return (*reinterpret_cast(this))[1](this, size); + } + + void FreeKeyValuesMemory(void* pMem) // @0x1413AA200 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, void*); + (*reinterpret_cast(this))[2](this, pMem); + } + + HKeySymbol GetSymbolForString(const char* name, bool bCreate) // @0x1413AA208 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = HKeySymbol(__thiscall*)(CKeyValuesSystem*, const char*, bool); + return (*reinterpret_cast(this))[3](this, name, bCreate); + } + + const char* GetStringForSymbol(HKeySymbol symbol) // @0x1413AA210 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = const char* (__thiscall*)(CKeyValuesSystem*, HKeySymbol); + return (*reinterpret_cast(this))[4](this, symbol); + } + + // void __fastcall CKeyValuesSystem::FreeKeyValuesMemory(CKeyValuesSystem* this_arg, void* ptr_mem_arg) + // { + // __int64* v2; // rax + // __int64 v4; // rax + // __int64* v5; // rax + // + // v2 = qword_14D40B538; + // if (!qword_14D40B538) + // { + // v2 = sub_140462930(); + // qword_14D40B538 = v2; + // } + // v4 = (*(*v2 + 48))(v2, ptr_mem_arg); + // if (v4 > 0) + // CKeyValuesSystem::m_pMemPool -= v4; + // v5 = qword_14D40B538; + // if (!qword_14D40B538) + // { + // v5 = sub_140462930(); + // qword_14D40B538 = v5; + // } + // (*(*v5 + 40))(v5, ptr_mem_arg); + // } + + // GetMemPool return a global variable called m_pMemPool it gets modified by AllocKeyValuesMemory and FreeKeyValuesMemory above you can see where the find it in FreeKeyValuesMemory. + void* GetMemPool() // @0x1413AA228 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + return reinterpret_cast(g_pKeyValuesMemPool); // May need to dereference this once more not sure right now. + } + + void SetKeyValuesExpressionSymbol(const char* name, bool bValue) // @0x1413AA230 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = void(__thiscall*)(CKeyValuesSystem*, const char*, bool); + (*reinterpret_cast(this))[8](this, name, bValue); + } + + bool GetKeyValuesExpressionSymbol(const char* name) // @0x1413AA238 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = bool(__thiscall*)(CKeyValuesSystem*, const char*); + return (*reinterpret_cast(this))[9](this, name); + } + + HKeySymbol GetSymbolForStringCaseSensitive(HKeySymbol& hCaseInsensitiveSymbol, const char* name, bool bCreate) // @0x1413AA240 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM + { + using OriginalFn = HKeySymbol(__thiscall*)(CKeyValuesSystem*, HKeySymbol&, const char*, bool); + return (*reinterpret_cast(this))[10](this, hCaseInsensitiveSymbol, name, bCreate); + } + +// Datatypes aren't accurate. But full fill the actual byte distance. +public: + void* vtable; // 0x0000 + std::int64_t m_iMaxKeyValuesSize; // 0x0008 +private: + char gap10[240]; // 0x0010 +public: + int m_KvConditionalSymbolTable; // 0x0100 +private: + char gap104[4]; // 0x0104 +public: + std::int64_t field_108; // 0x0108 +private: + char gap110[32]; // 0x0110 +public: + int m_mutex; // 0x0130 +}; + +class KeyValues +{ +public: + + KeyValues* FindKey(const char* keyName, bool bCreate) + { + static auto func = reinterpret_cast(KeyValues_FindKey); + return func(this, keyName, bCreate); + } + + const char* GetName(); + + int GetInt(const char* keyName, int defaultValue) + { + KeyValues* dat = FindKey(keyName, false); + + if (!dat) + return defaultValue; + + switch (dat->m_iDataType) + { + case TYPE_STRING: + return atoi(dat->m_sValue); + case TYPE_FLOAT: + return static_cast(m_flValue()); + case TYPE_WSTRING: + return _wtoi(dat->m_wsValue); + case TYPE_UINT64: + return 0; + default: + return dat->m_iValue(); + } + + return defaultValue; + } + + void SetInt(const char* keyName, int iValue) + { + KeyValues* dat = FindKey(keyName, true); + if (dat) + { + dat->m_iValue() = iValue; + dat->m_iDataType = TYPE_INT; + } + } + + void SetFloat(const char* keyName, float flValue) + { + KeyValues* dat = FindKey(keyName, true); + if (dat) + { + dat->m_flValue() = flValue; + dat->m_iDataType = TYPE_FLOAT; + } + } + + // Compiler makes it so m_Value shares the offset spot with m_flValue that why we cast it like this. + float& m_flValue() + { + static std::int32_t offset = 0x18; + return *(float*)((std::uintptr_t)this + offset); + } + + int& m_iValue() + { + static std::int32_t offset = 0x18; + return *(int*)((std::uintptr_t)this + offset); + } + +public: + uint32_t m_iKeyName : 24; // 0x0000 + uint32_t m_iKeyNameCaseSensitive : 8; // 0x0003 + char* m_sValue; // 0x0008 + wchar_t* m_wsValue; // 0x0010 + int m_nValue; // 0x0018 +private: + char gap1C[12]; // 0x0020 +public: + char m_iDataType; // 0x0028 + uint16_t m_iKeyNameCaseSensitive2; // 0x002A + KeyValues* m_pPeer; // 0x0030 + KeyValues* m_pSub; // 0x0038 + KeyValues* m_pChain; // 0x0040 +}; + +/////////////////////////////////////////////////////////////////////////////// +void CKeyValueSystem_InitPlaylist(); +void CKeyValueSystem_Init(); +bool HKeyValues_LoadPlaylist(const char* playlist); + +void CKeyValueSystem_Attach(); +void CKeyValueSystem_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +extern CKeyValuesSystem* g_pKeyValuesSystem; +extern KeyValues** g_pPlaylistKeyValues; + +/////////////////////////////////////////////////////////////////////////////// +class HKeyValues : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: KeyValues::Init : 0x" << std::hex << std::uppercase << p_KeyValues_Init.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: KeyValues::FindKey : 0x" << std::hex << std::uppercase << p_KeyValues_FindKey.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: KeyValues::LoadPlaylist : 0x" << std::hex << std::uppercase << p_KeyValues_LoadPlaylist.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: KeyValues::GetMemPool : 0x" << std::hex << std::uppercase << p_KeyValues_GetMemPool.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pKeyValuesMemPool : 0x" << std::hex << std::uppercase << g_pKeyValuesMemPool << std::setw(npad) << " |" << std::endl; + std::cout << "| VAR: g_pKeyValuesSystem : 0x" << std::hex << std::uppercase << g_pKeyValuesSystem << std::setw(0) << " |" << std::endl; + std::cout << "| VAR: g_pPlaylistKeyValues : 0x" << std::hex << std::uppercase << g_pPlaylistKeyValues << std::setw(0) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HKeyValues); diff --git a/r5dev/vphysics/QHull.cpp b/r5dev/vphysics/QHull.cpp new file mode 100644 index 00000000..b518c7ae --- /dev/null +++ b/r5dev/vphysics/QHull.cpp @@ -0,0 +1,28 @@ +#include "core/stdafx.h" +#include "vphysics/QHull.h" + +//----------------------------------------------------------------------------- +// Purpose: qhull error and debug prints +//----------------------------------------------------------------------------- +int HQHull_PrintError(char* fmt, va_list args) +{ + vprintf(fmt, args); + return QHull_PrintError(fmt, args); +} + +int HQHull_PrintDebug(char* fmt, va_list args) +{ + vprintf(fmt, args); + return QHull_PrintDebug(fmt, args); +} +void QHull_Attach() +{ + DetourAttach((LPVOID*)&QHull_PrintDebug, &HQHull_PrintDebug); + DetourAttach((LPVOID*)&QHull_PrintError, &HQHull_PrintError); +} + +void QHull_Detach() +{ + DetourDetach((LPVOID*)&QHull_PrintDebug, &HQHull_PrintDebug); + DetourDetach((LPVOID*)&QHull_PrintError, &HQHull_PrintError); +} diff --git a/r5dev/vphysics/QHull.h b/r5dev/vphysics/QHull.h new file mode 100644 index 00000000..79988035 --- /dev/null +++ b/r5dev/vphysics/QHull.h @@ -0,0 +1,31 @@ +#pragma once + +namespace +{ + ADDRESS p_QHull_PrintError = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x4C\x24\x08\x48\x89\x54\x24\x10\x4C\x89\x44\x24\x18\x4C\x89\x4C\x24\x20\x53\xB8\x40\x27\x00\x00\x00\x00\x00\x00\x00\x48", "xxxxxxxxxxxxxxxxxxxxxxxxxx????xx"); + int (*QHull_PrintError)(char* fmt, va_list args) = (int (*)(char*, va_list))p_QHull_PrintError.GetPtr(); /*48 89 4C 24 08 48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 53 B8 40 27 00 00 ?? ?? ?? ?? 00 48*/ + + ADDRESS p_QHull_PrintDebug = g_mGameDll.FindPatternSIMD((std::uint8_t*)"\x48\x89\x54\x24\x10\x4C\x89\x44\x24\x18\x4C\x89\x4C\x24\x20\x53\x56\x57\x48\x83\xEC\x30\x48\x8B\xFA\x48\x8D\x74\x24\x60\x48\x8B", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + int (*QHull_PrintDebug)(char* fmt, va_list args) = (int (*)(char*, va_list))p_QHull_PrintDebug.GetPtr(); /*48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 53 56 57 48 83 EC 30 48 8B FA 48 8D 74 24 60 48 8B*/ +} + +/////////////////////////////////////////////////////////////////////////////// +int HQHull_PrintError(char* fmt, va_list args); +int HQHull_PrintDebug(char* fmt, va_list args); + +void QHull_Attach(); +void QHull_Detach(); + +/////////////////////////////////////////////////////////////////////////////// +class HQHull : public IDetour +{ + virtual void debugp() + { + std::cout << "| FUN: QHull_PrintError : 0x" << std::hex << std::uppercase << p_QHull_PrintError.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "| FUN: QHull_PrintDebug : 0x" << std::hex << std::uppercase << p_QHull_PrintDebug.GetPtr() << std::setw(npad) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; + } +}; +/////////////////////////////////////////////////////////////////////////////// + +REGISTER(HQHull); diff --git a/r5dev/vpklib/packedstore.cpp b/r5dev/vpklib/packedstore.cpp new file mode 100644 index 00000000..9ec260dd --- /dev/null +++ b/r5dev/vpklib/packedstore.cpp @@ -0,0 +1,365 @@ +#include "core/stdafx.h" +#include "tier0/cvar.h" +#include "mathlib/adler32.h" +#include "mathlib/crc32.h" +#include "engine/sys_utils.h" +#include "vpklib/packedstore.h" + +/*********************************************************************** +* ██████╗ ██████╗ ██╗ ██╗██████╗ ██╗ ██╗ ██╗ ██╗██████╗ * +* ██╔══██╗╚════██╗ ██║ ██║██╔══██╗██║ ██╔╝ ██║ ██║██╔══██╗ * +* ██████╔╝ █████╔╝ ██║ ██║██████╔╝█████╔╝ ██║ ██║██████╔╝ * +* ██╔══██╗██╔═══╝ ╚██╗ ██╔╝██╔═══╝ ██╔═██╗ ██║ ██║██╔══██╗ * +* ██║ ██║███████╗ ╚████╔╝ ██║ ██║ ██╗ ███████╗██║██████╔╝ * +* ╚═╝ ╚═╝╚══════╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝╚═════╝ * +***********************************************************************/ + +//----------------------------------------------------------------------------- +// Purpose: initialize parameters for decompression algorithm +//----------------------------------------------------------------------------- +void CPackedStore::InitLzParams() +{ + /*| PARAMETERS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*/ + m_lzDecompParams.m_dict_size_log2 = RVPK_DICT_SIZE; + m_lzDecompParams.m_decompress_flags = LZHAM_DECOMP_FLAG_OUTPUT_UNBUFFERED | LZHAM_DECOMP_FLAG_COMPUTE_CRC32; + m_lzDecompParams.m_struct_size = sizeof(lzham_decompress_params); +} + +//----------------------------------------------------------------------------- +// Purpose: obtains archive chunk path for specific file +//----------------------------------------------------------------------------- +std::string CPackedStore::GetPackChunkFile(std::string svPackDirFile, int iArchiveIndex) +{ + /*| ARCHIVES ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*/ + std::string svPackChunkFile = StripLocalePrefix(svPackDirFile); + std::ostringstream oss; + + oss << std::setw(3) << std::setfill('0') << iArchiveIndex; + std::string svPackChunkIndex = "pak000_" + oss.str(); + + StringReplace(svPackChunkFile, "pak000_dir", svPackChunkIndex); + return svPackChunkFile; +} + +//----------------------------------------------------------------------------- +// Purpose: returns pupulated pack dir struct for specified pack dir file +//----------------------------------------------------------------------------- +vpk_dir_h CPackedStore::GetPackDirFile(std::string svPackDirFile) +{ + /*| PACKDIRFILE |||||||||||||||||||||||||||||||||||||||||||||||||||||||||*/ + std::regex rgArchiveRegex("pak000_([0-9]{3})"); + std::smatch smRegexMatches; + + std::regex_search(svPackDirFile, smRegexMatches, rgArchiveRegex); + + if (smRegexMatches.size() != 0) + { + StringReplace(svPackDirFile, smRegexMatches[0], "pak000_dir"); + + for (int i = 0; i < LANGUAGE_PACKS; i++) + { + if (strstr(svPackDirFile.c_str(), DIR_LIBRARY_PREFIX[i].c_str())) + { + for (int j = 0; j < LIBRARY_PACKS; j++) + { + if (strstr(svPackDirFile.c_str(), DIR_LIBRARY_PREFIX[j].c_str())) + { + std::string svPackDirPrefix = DIR_LOCALE_PREFIX[i] + DIR_LOCALE_PREFIX[i]; + StringReplace(svPackDirFile, DIR_LOCALE_PREFIX[i].c_str(), svPackDirPrefix.c_str()); + goto escape; + } + } + } + }escape:; + } + + vpk_dir_h vpk_dir(svPackDirFile); + return vpk_dir; +} + +//----------------------------------------------------------------------------- +// Purpose: obtains and returns the entry block to the vector +//----------------------------------------------------------------------------- +std::vector CPackedStore::GetEntryBlocks(CIOStream* reader) +{ + /*| ENTRYBLOCKS |||||||||||||||||||||||||||||||||||||||||||||||||||||||||*/ + std::string svName, svPath, svExtension; + std::vector vBlocks; + while (!(svExtension = reader->readString()).empty()) + { + while (!(svPath = reader->readString()).empty()) + { + while (!(svName = reader->readString()).empty()) + { + std::string svFilePath = FormatBlockPath(svName, svPath, svExtension); + vBlocks.push_back(vpk_entry_block(reader, svFilePath)); + } + } + } + return vBlocks; +} + +//----------------------------------------------------------------------------- +// Purpose: formats the entry block path +//----------------------------------------------------------------------------- +std::string CPackedStore::FormatBlockPath(std::string svName, std::string svPath, std::string svExtension) +{ + if (!svPath.empty()) + { + svPath += "\\"; + } + return svPath + svName + "." + svExtension; +} + +//----------------------------------------------------------------------------- +// Purpose: strips locale prefix from file path +//----------------------------------------------------------------------------- +std::string CPackedStore::StripLocalePrefix(std::string svPackDirFile) +{ + std::filesystem::path fspPackDirFile(svPackDirFile); + std::string svFileName = fspPackDirFile.filename().u8string(); + + for (int i = 0; i < LANGUAGE_PACKS; i++) + { + if (strstr(svFileName.c_str(), DIR_LOCALE_PREFIX[i].c_str())) + { + StringReplace(svFileName, DIR_LOCALE_PREFIX[i].c_str(), ""); + goto escape; + } + }escape:; + return svFileName; +} + +//----------------------------------------------------------------------------- +// Purpose: validates extraction result with precomputed ADLER32 hash +//----------------------------------------------------------------------------- +void CPackedStore::ValidateAdler32PostDecomp(std::string svAssetFile) +{ + uint32_t adler_init = {}; + std::ifstream istream(svAssetFile, std::fstream::binary); + + istream.seekg(0, std::fstream::end); + m_vHashBuffer.resize(istream.tellg()); + istream.seekg(0, std::fstream::beg); + istream.read((char*)m_vHashBuffer.data(), m_vHashBuffer.size()); + + m_nAdler32 = adler32::update(adler_init, m_vHashBuffer.data(), m_vHashBuffer.size()); + + if (m_nAdler32 != m_nAdler32_Internal) + { + DevMsg(eDLL_T::FS, "Warning: ADLER32 checksum mismatch for entry '%s' computed value '0x%lX' doesn't match expected value '0x%lX'. File may be corrupt!\n", svAssetFile.c_str(), m_nAdler32, m_nAdler32_Internal); + m_nAdler32 = 0; + m_nAdler32_Internal = 0; + } + + istream.close(); + m_vHashBuffer.clear(); +} + +//----------------------------------------------------------------------------- +// Purpose: validates extraction result with precomputed CRC32 hash +//----------------------------------------------------------------------------- +void CPackedStore::ValidateCRC32PostDecomp(std::string svDirAsset) +{ + uint32_t crc32_init = {}; + std::ifstream istream(svDirAsset, std::fstream::binary); + + istream.seekg(0, std::fstream::end); + m_vHashBuffer.resize(istream.tellg()); + istream.seekg(0, std::fstream::beg); + istream.read((char*)m_vHashBuffer.data(), m_vHashBuffer.size()); + + m_nCrc32 = crc32::update(crc32_init, m_vHashBuffer.data(), m_vHashBuffer.size()); + + if (m_nCrc32 != m_nCrc32_Internal) + { + DevMsg(eDLL_T::FS, "Warning: CRC32 checksum mismatch for entry '%s' computed value '0x%lX' doesn't match expected value '0x%lX'. File may be corrupt!\n", svDirAsset.c_str(), m_nCrc32, m_nCrc32_Internal); + m_nCrc32 = 0; + m_nCrc32_Internal = 0; + } + + istream.close(); + m_vHashBuffer.clear(); +} + +//----------------------------------------------------------------------------- +// Purpose: extracts all files from specified vpk file +//----------------------------------------------------------------------------- +void CPackedStore::UnpackAll(vpk_dir_h vpk_dir, std::string svPathOut) +{ + for (int i = 0; i < vpk_dir.m_vsvArchives.size(); i++) + { + std::filesystem::path fspVpkPath(vpk_dir.m_svDirPath); + std::string svPath = fspVpkPath.parent_path().u8string() + "\\" + vpk_dir.m_vsvArchives[i]; + std::ifstream packChunkStream(svPath, std::ios_base::binary); // Create stream to read from each archive. + + for ( vpk_entry_block block : vpk_dir.m_vvEntryBlocks) + { + if (block.m_iArchiveIndex != i) + { + // Continue if block archive index is not part of the extracting archive chunk index. + goto cont; + } + else + { + std::string svFilePath = CreateDirectories(svPathOut + "\\" + block.m_svBlockPath); + std::ofstream outFileStream(svFilePath, std::ios_base::binary | std::ios_base::out); + + if (!outFileStream.is_open()) + { + DevMsg(eDLL_T::FS, "Error: unable to access file '%s'!\n", svFilePath.c_str()); + } + outFileStream.clear(); // Make sure file is empty before writing. + for (vpk_entry_h entry : block.m_vvEntries) + { + char* pCompressedData = new char[entry.m_nCompressedSize]; + memset(pCompressedData, 0, entry.m_nCompressedSize); // Compressed region. + + packChunkStream.seekg(entry.m_nArchiveOffset); // Seek to entry offset in archive. + packChunkStream.read(pCompressedData, entry.m_nCompressedSize); // Read compressed data from archive. + + if (entry.m_bIsCompressed) + { + lzham_uint8* pLzOutputBuf = new lzham_uint8[entry.m_nUncompressedSize]; + m_lzDecompStatus = lzham_decompress_memory(&m_lzDecompParams, pLzOutputBuf, (size_t*)&entry.m_nUncompressedSize, (lzham_uint8*)pCompressedData, entry.m_nCompressedSize, &m_nAdler32_Internal, &m_nCrc32_Internal); + + if (fs_packedstore_entryblock_stats->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::FS, "--------------------------------------------------------------\n"); + DevMsg(eDLL_T::FS, "] Block path : '%s'\n", block.m_svBlockPath.c_str()); + DevMsg(eDLL_T::FS, "] Entry count : '%llu'\n", block.m_vvEntries.size()); + DevMsg(eDLL_T::FS, "] Compressed size : '%llu'\n", entry.m_nCompressedSize); + DevMsg(eDLL_T::FS, "] Uncompressed size : '%llu'\n", entry.m_nUncompressedSize); + DevMsg(eDLL_T::FS, "] Static CRC32 hash : '0x%lX'\n", block.m_nCrc32); + DevMsg(eDLL_T::FS, "] Computed CRC32 hash : '0x%lX'\n", m_nCrc32_Internal); + DevMsg(eDLL_T::FS, "] Computed ADLER32 hash : '0x%lX'\n", m_nAdler32_Internal); + DevMsg(eDLL_T::FS, "--------------------------------------------------------------\n"); + } + + if (block.m_vvEntries.size() == 1) // Internal checksum can only match block checksum if entry size is 1. + { + if (block.m_nCrc32 != m_nCrc32_Internal) + { + DevMsg(eDLL_T::FS, "Warning: CRC32 checksum mismatch for entry '%s' computed value '0x%lX' doesn't match expected value '0x%lX'. File may be corrupt!\n", block.m_svBlockPath.c_str(), m_nCrc32_Internal, block.m_nCrc32); + } + } + else { m_nEntryCount++; } + + if (m_lzDecompStatus != lzham_decompress_status_t::LZHAM_DECOMP_STATUS_SUCCESS) + { + DevMsg(eDLL_T::FS, "Error: failed decompression for an entry within block '%s' in archive '%d'!\n", block.m_svBlockPath.c_str(), i); + DevMsg(eDLL_T::FS, "'lzham_decompress_memory_func' returned with status '%d'.\n", m_lzDecompStatus); + } + else + { + // If successfully decompressed, write to file. + outFileStream.write((char*)pLzOutputBuf, entry.m_nUncompressedSize); + delete[] pLzOutputBuf, pCompressedData; + } + } + else + { + // If not compressed, write raw data into output file. + outFileStream.write(pCompressedData, entry.m_nUncompressedSize); + } + } + outFileStream.close(); + if (m_nEntryCount == block.m_vvEntries.size()) // Only validate after last entry in block had been written. + { + // Set internal hash to precomputed entry hash for post decompress validation. + m_nCrc32_Internal = block.m_nCrc32; + + ValidateCRC32PostDecomp(svFilePath); + //ValidateAdler32PostDecomp(svFilePath); + m_nEntryCount = 0; + } + }cont:; + } + packChunkStream.close(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: 'vpk_entry_block' constructor +//----------------------------------------------------------------------------- +vpk_entry_block::vpk_entry_block(CIOStream* reader, std::string svPath) +{ + std::replace(svPath.begin(), svPath.end(), '/', '\\'); // Flip forward slashes in filepath to windows-style backslash. + + this->m_svBlockPath = svPath; // Set path of block. + reader->read(this->m_nCrc32); // + reader->read(this->m_nPreloadBytes); // + reader->read(this->m_iArchiveIndex); // + + do // Loop through all entries in the block and push them to the vector. + { + vpk_entry_h entry(reader); + this->m_vvEntries.push_back(entry); + } while (reader->readR() != 65535); +} + +//----------------------------------------------------------------------------- +// Purpose: 'vpk_entry_h' constructor +//----------------------------------------------------------------------------- +vpk_entry_h::vpk_entry_h(CIOStream* reader) +{ + reader->read(this->m_nEntryFlags); // + reader->read(this->m_nTextureFlags); // + reader->read(this->m_nArchiveOffset); // + reader->read(this->m_nCompressedSize); // + reader->read(this->m_nUncompressedSize); // + this->m_bIsCompressed = (this->m_nCompressedSize != this->m_nUncompressedSize); +} + +//----------------------------------------------------------------------------- +// Purpose: 'vpk_dir_h' constructor +//----------------------------------------------------------------------------- +vpk_dir_h::vpk_dir_h(std::string svPath) +{ + CIOStream reader; + + reader.open(svPath, eStreamFileMode::READ); + reader.read(this->m_nFileMagic); + + if (this->m_nFileMagic != RVPK_DIR_MAGIC) + { + DevMsg(eDLL_T::FS, "Error: vpk_dir file '%s' has invalid magic!\n", svPath.c_str()); + return; + } + + reader.read(this->m_nMajorVersion); // + reader.read(this->m_nMinorVersion); // + reader.read(this->m_nTreeSize); // + reader.read(this->m_nFileDataSize); // + + DevMsg(eDLL_T::FS, "______________________________________________________________\n"); + DevMsg(eDLL_T::FS, "] HEADER_DETAILS ---------------------------------------------\n"); + DevMsg(eDLL_T::FS, "] File Magic : '%lu'\n", this->m_nFileMagic); + DevMsg(eDLL_T::FS, "] Major Version : '%hu'\n", (this->m_nMajorVersion)); + DevMsg(eDLL_T::FS, "] Minor Version : '%hu'\n", (this->m_nMinorVersion)); + DevMsg(eDLL_T::FS, "] Tree Size : '%lu'\n", this->m_nTreeSize); + DevMsg(eDLL_T::FS, "] File Data Size : '%lu'\n", this->m_nFileDataSize); + + this->m_vvEntryBlocks = g_pPackedStore->GetEntryBlocks(&reader); + this->m_svDirPath = svPath; // Set path to vpk_dir file. + + for (vpk_entry_block block : this->m_vvEntryBlocks) + { + if (block.m_iArchiveIndex > this->m_iArchiveCount) + { + this->m_iArchiveCount = block.m_iArchiveIndex; + } + } + + DevMsg(eDLL_T::FS, "______________________________________________________________\n"); + DevMsg(eDLL_T::FS, "] PACK_CHUNKS ------------------------------------------------\n"); + + for (int i = 0; i < this->m_iArchiveCount + 1; i++) + { + std::string svArchivePath = g_pPackedStore->GetPackChunkFile(svPath, i); + DevMsg(eDLL_T::FS, "] '%s\n", svArchivePath.c_str()); + this->m_vsvArchives.push_back(svArchivePath); + } +} +/////////////////////////////////////////////////////////////////////////////// +CPackedStore* g_pPackedStore = new CPackedStore(); diff --git a/r5dev/vpklib/packedstore.h b/r5dev/vpklib/packedstore.h new file mode 100644 index 00000000..977912bd --- /dev/null +++ b/r5dev/vpklib/packedstore.h @@ -0,0 +1,74 @@ +#pragma once +#include "public/include/binstream.h" +#include "thirdparty/lzham/include/lzham.h" + +constexpr unsigned int LIBRARY_PACKS = 2; +constexpr unsigned int LANGUAGE_PACKS = 11; +constexpr unsigned int RVPK_DICT_SIZE = 20; +constexpr unsigned int RVPK_DIR_MAGIC = 'U4'; + +const std::string DIR_LIBRARY_PREFIX[LIBRARY_PACKS] = { "server", "client" }; +const std::string DIR_LOCALE_PREFIX[LANGUAGE_PACKS] = { "english", "french", "german", "italian", "japanese", "korean", "polish", "portuguese", "russian", "spanish", "tchinese" }; + +struct vpk_entry_h +{ + uint32_t m_nEntryFlags {}; // Entry flags. + uint16_t m_nTextureFlags {}; // Texture flags (only used if the entry is a vtf). + uint64_t m_nArchiveOffset {}; // Offset in archive. + uint64_t m_nCompressedSize {}; // Compressed size of entry. + uint64_t m_nUncompressedSize{}; // Uncompressed size of entry. + bool m_bIsCompressed = false; + + vpk_entry_h(CIOStream* reader); +}; + +struct vpk_entry_block +{ + uint32_t m_nCrc32 {}; // Crc32 for the uncompressed block. + uint16_t m_nPreloadBytes{}; // Preload bytes. + uint16_t m_iArchiveIndex{}; // Index of the archive that contains this block. + std::vector m_vvEntries {}; // Vector of all the entries of a given block (entries have a size limit of 1 MiB, so anything over is split into separate entries within the same block). + std::string m_svBlockPath {}; // Path to block within vpk. + + vpk_entry_block(CIOStream* reader, std::string path); +}; + +struct vpk_dir_h +{ + uint32_t m_nFileMagic {}; // File magic. + uint16_t m_nMajorVersion{}; // Vpk major version. + uint16_t m_nMinorVersion{}; // Vpk minor version. + uint32_t m_nTreeSize {}; // Directory tree size. + uint32_t m_nFileDataSize{}; // File data section size. + std::vector m_vvEntryBlocks{}; // Vector of entry blocks. + uint16_t m_iArchiveCount{}; // Highest archive index (archive count-1). + std::vector m_vsvArchives {}; // Vector of archive file names. + std::string m_svDirPath {}; // Path to vpk_dir file. + + vpk_dir_h(std::string path); +}; + +class CPackedStore +{ + std::vector m_vHashBuffer {}; // Buffer for post decomp file validation. + std::size_t m_nEntryCount {}; // Entry per-block incrementor. + lzham_uint32 m_nAdler32_Internal{}; // Internal operation Adler32 file checksum. + lzham_uint32 m_nAdler32 {}; // Pre/post operation Adler32 file checksum. + lzham_uint32 m_nCrc32_Internal {}; // Internal operation Crc32 file checksum. + lzham_uint32 m_nCrc32 {}; // Pre/post operation Crc32 file checksum. + lzham_decompress_params m_lzDecompParams {}; // LZham decompression parameters. + lzham_decompress_status_t m_lzDecompStatus {}; // LZham decompression results. + +public: + void InitLzParams(); + vpk_dir_h GetPackDirFile(std::string svPackDirFile); + std::string GetPackChunkFile(std::string svPackDirFile, int iArchiveIndex); + std::vector GetEntryBlocks(CIOStream* reader); + std::string FormatBlockPath(std::string svName, std::string svPath, std::string svExtension); + std::string StripLocalePrefix(std::string svPackDirFile); + void UnpackAll(vpk_dir_h vpk, std::string svPathOut = ""); + void ValidateAdler32PostDecomp(std::string svDirAsset); + void ValidateCRC32PostDecomp(std::string svDirAsset); +}; +/////////////////////////////////////////////////////////////////////////////// +extern CPackedStore* g_pPackedStore; diff --git a/r5dev/src/console.cpp b/r5dev/windows/console.cpp similarity index 62% rename from r5dev/src/console.cpp rename to r5dev/windows/console.cpp index 7ee85e3a..96927d05 100644 --- a/r5dev/src/console.cpp +++ b/r5dev/windows/console.cpp @@ -1,63 +1,22 @@ -#include "pch.h" -#include "id3dx.h" -#include "hooks.h" -#include "opcptc.h" -#include "console.h" -#include "patterns.h" - -//############################################################################# -// WORKER THREAD -//############################################################################# - -DWORD __stdcall ProcessConsoleWorker(LPVOID) -{ - while (true) // Loop forever - { - std::string sCommand = std::string(); - - /////////////////////////////////////////////////////////////////////// - // Get the user input on the debug console - std::getline(std::cin, sCommand); - - /////////////////////////////////////////////////////////////////////// - // Engine toggles - if (sCommand == "toggle net") { Hooks::ToggleNetTrace(); continue; } - if (sCommand == "toggle dev") { Hooks::ToggleDevCommands(); continue; } - if (sCommand == "toggle fal") { g_bReturnAllFalse = !g_bReturnAllFalse; continue; } - - /////////////////////////////////////////////////////////////////////// - // Debug toggles - if (sCommand == "pattern test") { PrintHAddress(); PrintOAddress(); continue; } - if (sCommand == "directx test") { PrintDXAddress(); continue; } - if (sCommand == "console test") { g_bDebugConsole = !g_bDebugConsole; continue; } - - /////////////////////////////////////////////////////////////////////// - // Exec toggles - if (sCommand == "1") { Hooks::ToggleDevCommands(); addr_CommandExecute(NULL, "exec autoexec_dev"); } - if (sCommand == "2") { g_bDebugLoading = !g_bDebugLoading; continue; } - - /////////////////////////////////////////////////////////////////////// - // Execute the command in the r5 SQVM - addr_CommandExecute(NULL, sCommand.c_str()); - sCommand.clear(); - - /////////////////////////////////////////////////////////////////////// - // Sleep and loop - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - - return 0; -} +#include "core/stdafx.h" +#include "core/init.h" +#include "rtech/rtech.h" +#ifndef DEDICATED +#include "windows/id3dx.h" +#endif // !DEDICATED +#include "windows/console.h" +#include "client/IVEngineClient.h" +#include "common/opcodes.h" //############################################################################# // INITIALIZATION //############################################################################# -void SetupConsole() +void Console_Init() { /////////////////////////////////////////////////////////////////////////// // Create the console window - if (!AllocConsole()) + if (AllocConsole() == FALSE) { OutputDebugString("Failed to create console window!\n"); return; @@ -69,6 +28,7 @@ void SetupConsole() CHAR sBuildBuf[1024] = { 0 }; fopen_s(&sBuildTxt, "build.txt", "r"); + if (sBuildTxt) { while (fgets(sBuildBuf, sizeof(sBuildBuf), sBuildTxt) != NULL) @@ -87,24 +47,53 @@ void SetupConsole() /////////////////////////////////////////////////////////////////////////// // Create a worker thread to process console commands - DWORD threadID = NULL; - HANDLE hThread = CreateThread(NULL, 0, ProcessConsoleWorker, NULL, 0, &threadID); + DWORD threadId; + DWORD __stdcall ProcessConsoleWorker(LPVOID); + HANDLE hThread = CreateThread(NULL, 0, ProcessConsoleWorker, NULL, 0, &threadId); // Initialize global spdlog. auto console = spdlog::stdout_logger_mt("console"); - console->set_pattern("[%I:%M:%S:%e] [%L] %v"); // Set pattern. + console->set_pattern("[%S.%e] %v"); // Set pattern. + + spdlog::set_level(spdlog::level::trace); spdlog::set_default_logger(console); // Set as default. spdlog::flush_every(std::chrono::seconds(5)); // Flush buffers every 5 seconds for every logger. - -#ifdef _DEBUG - console->set_level(spdlog::level::debug); -#endif - - spdlog::debug("Console and spdlog are setup now!\n"); - + if (hThread) { - spdlog::info("THREAD ID: {}\n\n", threadID); + spdlog::debug("THREAD ID: {}\n\n", threadId); CloseHandle(hThread); } -} \ No newline at end of file +} + +//############################################################################# +// WORKER THREAD +//############################################################################# + +DWORD __stdcall ProcessConsoleWorker(LPVOID) +{ + // Loop forever + while (true) + { + static std::string sCommand = ""; + + /////////////////////////////////////////////////////////////////////// + // Get the user input on the debug console + printf(">"); + std::getline(std::cin, sCommand); + + /////////////////////////////////////////////////////////////////////// + // Debug toggles + if (sCommand == "pattern test") { PrintHAddress(); PrintOAddress(); continue; } + if (sCommand == "opcodes test") { RuntimePtc_Toggle(); continue; } + /////////////////////////////////////////////////////////////////////// + // Execute the command in the r5 SQVM + IVEngineClient_CommandExecute(NULL, sCommand.c_str()); + sCommand.clear(); + + /////////////////////////////////////////////////////////////////////// + // Sleep and loop + Sleep(50); + } + return NULL; +} diff --git a/r5dev/windows/console.h b/r5dev/windows/console.h new file mode 100644 index 00000000..d96a8035 --- /dev/null +++ b/r5dev/windows/console.h @@ -0,0 +1,3 @@ +#pragma once + +void Console_Init(); diff --git a/r5dev/windows/id3dx.cpp b/r5dev/windows/id3dx.cpp new file mode 100644 index 00000000..5d30e2c8 --- /dev/null +++ b/r5dev/windows/id3dx.cpp @@ -0,0 +1,563 @@ +#include "core/stdafx.h" +#ifndef DEDICATED // This file should not be compiled for DEDICATED! +//------------------------------ +#define STB_IMAGE_IMPLEMENTATION +#include "tier0/cvar.h" +#include "windows/id3dx.h" +#include "windows/input.h" +#include "gameui/IConsole.h" +#include "gameui/IBrowser.h" +#include "engine/sys_utils.h" +#include "inputsystem/inputsystem.h" +#include "public/include/stb_image.h" + +/********************************************************************************** +----------------------------------------------------------------------------------- +File : id3dx.cpp +Date : 15:06:2021 +Author : Kawe Mazidjatari +Purpose: Microsoft DirectX 11 'IDXGISwapChain::Present' hook implementation +----------------------------------------------------------------------------------- +History: +- 15:06:2021 | 14:56 : Created by Kawe Mazidjatari +- 17:06:2021 | 13:12 : Destroy / release objects with 'GetResizeBuffers' callback + +**********************************************************************************/ + +/////////////////////////////////////////////////////////////////////////////////// +typedef BOOL(WINAPI* IPostMessageA)(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +typedef BOOL(WINAPI* IPostMessageW)(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + +/////////////////////////////////////////////////////////////////////////////////// +extern BOOL g_bShowConsole = false; +extern BOOL g_bShowBrowser = false; +static BOOL g_bInitMenu = false; +static BOOL g_bInitialized = false; +static BOOL g_bPresentHooked = false; + +/////////////////////////////////////////////////////////////////////////////////// +static WNDPROC g_oWndProc = NULL; +static HWND g_hGameWindow = NULL; +extern DWORD g_dThreadId = NULL; + +/////////////////////////////////////////////////////////////////////////////////// +static IPostMessageA g_oPostMessageA = NULL; +static IPostMessageW g_oPostMessageW = NULL; + +/////////////////////////////////////////////////////////////////////////////////// +static IDXGIResizeBuffers g_oResizeBuffers = NULL; +static IDXGISwapChainPresent g_fnIDXGISwapChainPresent = NULL; +static IDXGISwapChain* g_pSwapChain = nullptr; +static ID3D11DeviceContext* g_pDeviceContext = nullptr; +static ID3D11Device* g_pDevice = nullptr; +static ID3D11RenderTargetView* g_pRenderTargetView = nullptr; +static ID3D11DepthStencilView* g_pDepthStencilView = nullptr; + +//################################################################################# +// WINDOW PROCEDURE +//################################################################################# + +LRESULT CALLBACK DXGIMsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +LRESULT CALLBACK HwndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN) + { + if (wParam == g_pImGuiConfig->IConsole_Config.m_nBind0 || wParam == g_pImGuiConfig->IConsole_Config.m_nBind1) + { + g_bShowConsole = !g_bShowConsole; + } + + if (wParam == g_pImGuiConfig->IBrowser_Config.m_nBind0 || wParam == g_pImGuiConfig->IBrowser_Config.m_nBind1) + { + g_bShowBrowser = !g_bShowBrowser; + } + } + + if (g_bShowConsole || g_bShowBrowser) + {////////////////////////////////////////////////////////////////////////////// + ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam); + g_bBlockInput = true; + + switch (uMsg) + { + case WM_LBUTTONDOWN: + return 1L; + case WM_LBUTTONUP: + return 1L; + case WM_LBUTTONDBLCLK: + return 1L; + case WM_RBUTTONDOWN: + return 1L; + case WM_RBUTTONUP: + return 1L; + case WM_RBUTTONDBLCLK: + return 1L; + case WM_MBUTTONDOWN: + return 1L; + case WM_MBUTTONUP: + return 1L; + case WM_MBUTTONDBLCLK: + return 1L; + case WM_KEYDOWN: + return 1L; + case WM_KEYUP: + return 1L; + case WM_MOUSEACTIVATE: + return 1L; + case WM_MOUSEHOVER: + return 1L; + case WM_MOUSEHWHEEL: + return 1L; + case WM_MOUSELEAVE: + return 1L; + case WM_MOUSEMOVE: + return 1L; + case WM_MOUSEWHEEL: + return 1L; + case WM_SETCURSOR: + return 1L; + default: + break; + } + }////////////////////////////////////////////////////////////////////////////// + else + { + g_bBlockInput = false; + } + + /////////////////////////////////////////////////////////////////////////////// + return CallWindowProc(g_oWndProc, hWnd, uMsg, wParam, lParam); +} + +//################################################################################# +// POST MESSAGE +//################################################################################# + +BOOL WINAPI HPostMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + if (g_bBlockInput && Msg == WM_MOUSEMOVE) + { + return TRUE; + } + + return g_oPostMessageA(hWnd, Msg, wParam, lParam); +} + +BOOL WINAPI HPostMessageW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + if (g_bBlockInput && Msg == WM_MOUSEMOVE) + { + return TRUE; + } + + return g_oPostMessageW(hWnd, Msg, wParam, lParam); +} + +//################################################################################# +// IDXGI PRESENT +//################################################################################# + +void GetPresent() +{ + WNDCLASSEXA wc = { sizeof(WNDCLASSEX), CS_CLASSDC, DXGIMsgProc, 0L, 0L, GetModuleHandleA(NULL), NULL, NULL, NULL, NULL, "DX", NULL }; + RegisterClassExA(&wc); + + HWND hWnd = CreateWindowA("DX", NULL, WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, NULL, NULL, wc.hInstance, NULL); + DXGI_SWAP_CHAIN_DESC sd = { 0 }; + D3D_FEATURE_LEVEL nFeatureLevelsSet = D3D_FEATURE_LEVEL_11_0; + D3D_FEATURE_LEVEL nFeatureLevelsSupported; + + ZeroMemory(&sd, sizeof(sd)); + + /////////////////////////////////////////////////////////////////////////////// + sd.BufferCount = 1; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + sd.BufferDesc.Height = 800; + sd.BufferDesc.Width = 600; + sd.BufferDesc.RefreshRate = { 60, 1 }; + sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + sd.Windowed = TRUE; + sd.OutputWindow = hWnd; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + /////////////////////////////////////////////////////////////////////////////// + g_hGameWindow = sd.OutputWindow; + UINT nFeatureLevelsRequested = 1; + HRESULT hr = 0; + IDXGISwapChain* pSwapChain = nullptr; + ID3D11Device* pDevice = nullptr; + ID3D11DeviceContext* pContext = nullptr; + + /////////////////////////////////////////////////////////////////////////////// + if (FAILED(hr = D3D11CreateDeviceAndSwapChain(NULL, + D3D_DRIVER_TYPE_HARDWARE, + NULL, + NULL, + &nFeatureLevelsSet, + nFeatureLevelsRequested, + D3D11_SDK_VERSION, + &sd, + &pSwapChain, + &pDevice, + &nFeatureLevelsSupported, + &pContext))) + { + if (mat_showdxoutput->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::MS, "+--------------------------------------------------------+\n"); + DevMsg(eDLL_T::MS, "| >>>>>>>>>| VIRTUAL METHOD TABLE HOOK FAILED |<<<<<<<<< |\n"); + DevMsg(eDLL_T::MS, "+--------------------------------------------------------+\n"); + } + DirectX_Shutdown(); + return; + } + + /////////////////////////////////////////////////////////////////////////////// + DWORD_PTR* pSwapChainVtable = nullptr; + DWORD_PTR* pContextVTable = nullptr; + DWORD_PTR* pDeviceVTable = nullptr; + + pSwapChainVtable = (DWORD_PTR*)pSwapChain; + pSwapChainVtable = (DWORD_PTR*)pSwapChainVtable[0]; + pContextVTable = (DWORD_PTR*)pContext; + pContextVTable = (DWORD_PTR*)pContextVTable[0]; + pDeviceVTable = (DWORD_PTR*)pDevice; + pDeviceVTable = (DWORD_PTR*)pDeviceVTable[0]; + + int pIDX = (int)DXGISwapChainVTbl::Present; + int rIDX = (int)DXGISwapChainVTbl::ResizeBuffers; + + g_fnIDXGISwapChainPresent = (IDXGISwapChainPresent)(DWORD_PTR)pSwapChainVtable[pIDX]; + g_oResizeBuffers = (IDXGIResizeBuffers)(DWORD_PTR)pSwapChainVtable[rIDX]; + + pSwapChain->Release(); + pContext->Release(); + pDevice->Release(); + + /////////////////////////////////////////////////////////////////////////////// + g_bPresentHooked = true; +} + +//################################################################################# +// INITIALIZATION +//################################################################################# + +void SetupImGui() +{ + /////////////////////////////////////////////////////////////////////////////// + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGui_ImplWin32_Init(g_hGameWindow); + ImGui_ImplDX11_Init(g_pDevice, g_pDeviceContext); + ImGui::GetIO().ImeWindowHandle = g_hGameWindow; + + /////////////////////////////////////////////////////////////////////////////// + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_IsSRGB; +} + +void DrawImGui() +{ + bool bShowConsole = g_bShowConsole; + bool bShowBrowser = g_bShowBrowser; + + ImGui_ImplDX11_NewFrame(); + ImGui_ImplWin32_NewFrame(); + + ImGui::NewFrame(); + + if (g_bShowConsole) + { + g_pInputSystem->EnableInput(false); // Disable input to game when console is drawn. + DrawConsole(&bShowConsole); + } + if (g_bShowBrowser) + { + g_pInputSystem->EnableInput(false); // Disable input to game when browser is drawn. + DrawBrowser(&bShowBrowser); + } + if (!g_bShowConsole && !g_bShowBrowser) + { + g_pInputSystem->EnableInput(true); // Enable input to game when both are not drawn. + } + + ImGui::EndFrame(); + ImGui::Render(); + + g_pDeviceContext->OMSetRenderTargets(1, &g_pRenderTargetView, NULL); + ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); +} + +void CreateRenderTarget(IDXGISwapChain* pSwapChain) +{ + /////////////////////////////////////////////////////////////////////////////// + DXGI_SWAP_CHAIN_DESC sd {}; + D3D11_RENDER_TARGET_VIEW_DESC rd {}; + ID3D11Texture2D* pBackBuffer = nullptr; + + /////////////////////////////////////////////////////////////////////////////// + pSwapChain->GetDesc(&sd); + ZeroMemory(&rd, sizeof(rd)); + + g_hGameWindow = sd.OutputWindow; + rd.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + rd.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + + /////////////////////////////////////////////////////////////////////////////// + pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer); + if (pBackBuffer != NULL) { g_pDevice->CreateRenderTargetView(pBackBuffer, &rd, &g_pRenderTargetView); } + g_pDeviceContext->OMSetRenderTargets(1, &g_pRenderTargetView, NULL); + pBackBuffer->Release(); +} + +void CreateViewPort( UINT nWidth, UINT nHeight) +{ + float width = *(float*)(&nWidth); + float height = *(float*)(&nHeight); + + D3D11_VIEWPORT vp{}; + + /////////////////////////////////////////////////////////////////////////////// + vp.Width = width; + vp.Height = height; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + + /////////////////////////////////////////////////////////////////////////////// + g_pDeviceContext->RSSetViewports(1, &vp); +} + +void DestroyRenderTarget() +{ + if (g_pRenderTargetView != nullptr) + { + g_pRenderTargetView->Release(); + g_pRenderTargetView = nullptr; + g_pDeviceContext->OMSetRenderTargets(0, 0, 0); + + if (mat_showdxoutput->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::MS, "+--------------------------------------------------------+\n"); + DevMsg(eDLL_T::MS, "| >>>>>>>>>>>>>>| RENDER TARGET DESTROYED |<<<<<<<<<<<<< |\n"); + DevMsg(eDLL_T::MS, "+--------------------------------------------------------+\n"); + } + } +} + +//################################################################################# +// INTERNALS +//################################################################################# + +HRESULT GetDeviceAndCtxFromSwapchain(IDXGISwapChain* pSwapChain, ID3D11Device** ppDevice, ID3D11DeviceContext** ppContext) +{ + HRESULT ret = pSwapChain->GetDevice(__uuidof(ID3D11Device), (PVOID*)ppDevice); + if (SUCCEEDED(ret)) + { + (*ppDevice)->GetImmediateContext(ppContext); + } + return ret; +} + +HRESULT __stdcall GetResizeBuffers(IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags) +{ + g_bShowConsole = false; + g_bShowBrowser = false; + g_bInitialized = false; + g_bPresentHooked = false; + + /////////////////////////////////////////////////////////////////////////////// + DestroyRenderTarget(); + + /////////////////////////////////////////////////////////////////////////////// + return g_oResizeBuffers(pSwapChain, nBufferCount, nWidth, nHeight, dxFormat, nSwapChainFlags); +} + +HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags) +{ + if (!g_bInitialized) + { + if (FAILED(GetDeviceAndCtxFromSwapchain(pSwapChain, &g_pDevice, &g_pDeviceContext))) + { + if (mat_showdxoutput->m_pParent->m_iValue > 0) + { + DevMsg(eDLL_T::MS, "+--------------------------------------------------------+\n"); + DevMsg(eDLL_T::MS, "| >>>>>>>>>>| GET DVS AND CTX FROM SCP FAILED |<<<<<<<<< |\n"); + DevMsg(eDLL_T::MS, "+--------------------------------------------------------+\n"); + } + return g_fnIDXGISwapChainPresent(pSwapChain, nSyncInterval, nFlags); + } + + CreateRenderTarget(pSwapChain); + SetupImGui(); + + if (g_oWndProc == nullptr) + { // Only initialize HwndProc pointer once to avoid stack overflow during ResizeBuffers(..) + g_oWndProc = (WNDPROC)SetWindowLongPtr(g_hGameWindow, GWLP_WNDPROC, (LONG_PTR)HwndProc); + } + + g_bInitialized = true; + g_pSwapChain = pSwapChain; + g_pImGuiConfig->Load(); // Load ImGui configs. + } + + DrawImGui(); + g_bInitialized = true; + /////////////////////////////////////////////////////////////////////////////// + return g_fnIDXGISwapChainPresent(pSwapChain, nSyncInterval, nFlags); +} + +bool LoadTextureBuffer(unsigned char* buffer, int len, ID3D11ShaderResourceView** out_srv, int* out_width, int* out_height) +{ + // Load PNG buffer to a raw RGBA buffer + int image_width = 0; + int image_height = 0; + unsigned char* image_data = stbi_load_from_memory(buffer, len, &image_width, &image_height, NULL, 4); + + if (image_data == NULL) + { + assert(image_data == NULL); + return false; + } + + /////////////////////////////////////////////////////////////////////////////// + ID3D11Texture2D* pTexture = NULL; + D3D11_TEXTURE2D_DESC desc; + D3D11_SUBRESOURCE_DATA subResource; + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + + /////////////////////////////////////////////////////////////////////////////// + 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; + + /////////////////////////////////////////////////////////////////////////////// + subResource.pSysMem = image_data; + subResource.SysMemPitch = desc.Width * 4; + subResource.SysMemSlicePitch = 0; + g_pDevice->CreateTexture2D(&desc, &subResource, &pTexture); + + // Create texture view + 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; + + if (pTexture) + { + g_pDevice->CreateShaderResourceView(pTexture, &srvDesc, out_srv); + pTexture->Release(); + } + + *out_width = image_width; + *out_height = image_height; + stbi_image_free(image_data); + + return true; +} + +//################################################################################# +// MANAGEMENT +//################################################################################# + +void InstallDXHooks() +{ + /////////////////////////////////////////////////////////////////////////////// + g_oPostMessageA = (IPostMessageA)DetourFindFunction("user32.dll", "PostMessageA"); + g_oPostMessageW = (IPostMessageW)DetourFindFunction("user32.dll", "PostMessageW"); + + // Begin the detour transaction + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + // Hook PostMessage + DetourAttach(&(LPVOID&)g_oPostMessageA, (PBYTE)HPostMessageA); + DetourAttach(&(LPVOID&)g_oPostMessageW, (PBYTE)HPostMessageW); + + // Hook SwapChain + DetourAttach(&(LPVOID&)g_fnIDXGISwapChainPresent, (PBYTE)Present); + DetourAttach(&(LPVOID&)g_oResizeBuffers, (PBYTE)GetResizeBuffers); + + // Commit the transaction + if (DetourTransactionCommit() != NO_ERROR) + { + // Failed to hook into the process, terminate + TerminateProcess(GetCurrentProcess(), 0xBAD0C0DE); + } +} + +void DirectX_Shutdown() +{ + // Begin the detour transaction + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + // Unhook PostMessage + DetourDetach(&(LPVOID&)g_oPostMessageA, (PBYTE)HPostMessageA); + DetourDetach(&(LPVOID&)g_oPostMessageW, (PBYTE)HPostMessageW); + + // Unhook SwapChain + DetourDetach(&(LPVOID&)g_fnIDXGISwapChainPresent, (PBYTE)Present); + DetourDetach(&(LPVOID&)g_oResizeBuffers, (PBYTE)GetResizeBuffers); + + // Commit the transaction + DetourTransactionCommit(); + + /////////////////////////////////////////////////////////////////////////////// + // Shutdown ImGui + ImGui_ImplWin32_Shutdown(); + ImGui_ImplDX11_Shutdown(); +} + +void HIDXGI::debugp() +{ + /////////////////////////////////////////////////////////////////////////////// + std::cout << "| VAR: ID3D11DeviceContext : " << std::hex << std::uppercase << g_pDeviceContext << std::setw(4) << " |" << std::endl; + std::cout << "| VAR: ID3D11Device : " << std::hex << std::uppercase << g_pDevice << std::setw(4) << " |" << std::endl; + std::cout << "| VAR: ID3D11RenderTargetView : " << std::hex << std::uppercase << g_pRenderTargetView << std::setw(4) << " |" << std::endl; + std::cout << "| VAR: IDXGISwapChain : " << std::hex << std::uppercase << g_pSwapChain << std::setw(4) << " |" << std::endl; + std::cout << "| VAR: IDXGISwapChainPresent : " << std::hex << std::uppercase << g_fnIDXGISwapChainPresent << std::setw(4) << " |" << std::endl; + std::cout << "+----------------------------------------------------------------+" << std::endl; +} + +//################################################################################# +// ENTRYPOINT +//################################################################################# + +DWORD __stdcall DXSwapChainWorker(LPVOID) +{ + GetPresent(); + InstallDXHooks(); + return true; +} + +void DirectX_Init() +{ + // Create a worker thread for the in-game console frontend + DWORD __stdcall DXSwapChainWorker(LPVOID); + HANDLE hThread = CreateThread(NULL, 0, DXSwapChainWorker, NULL, 0, &g_dThreadId); + + if (hThread) + { + CloseHandle(hThread); + } +} +#endif // !DEDICATED diff --git a/r5dev/windows/id3dx.h b/r5dev/windows/id3dx.h new file mode 100644 index 00000000..3a773db8 --- /dev/null +++ b/r5dev/windows/id3dx.h @@ -0,0 +1,121 @@ +#pragma once +#ifndef DEDICATED // This file should not be compiled for DEDICATED! +//------------------------------ +#include + +///////////////////////////////////////////////////////////////////////////// +// Initialization +void SetupImGui(); +void DirectX_Init(); +void DrawImGui(); +void DestroyRenderTarget(); + +///////////////////////////////////////////////////////////////////////////// +// Internals +void InstallDXHooks(); +void DirectX_Shutdown(); + +extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +extern HRESULT __stdcall Present(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags); +//extern bool LoadTextureBuffer(unsigned char* image_data, const int& image_width, const int& image_height, ID3D11ShaderResourceView** out_srv); +extern bool LoadTextureBuffer(unsigned char* buffer, int len, ID3D11ShaderResourceView** out_srv, int* out_width, int* out_height); + +///////////////////////////////////////////////////////////////////////////// +// Typedefs +typedef HRESULT(__stdcall* IDXGISwapChainPresent)(IDXGISwapChain* pSwapChain, UINT nSyncInterval, UINT nFlags); +typedef HRESULT(__stdcall* IDXGIResizeBuffers) (IDXGISwapChain* pSwapChain, UINT nBufferCount, UINT nWidth, UINT nHeight, DXGI_FORMAT dxFormat, UINT nSwapChainFlags); + +///////////////////////////////////////////////////////////////////////////// +// Globals +extern DWORD g_dThreadId; +extern BOOL g_bShowConsole; +extern BOOL g_bShowBrowser; + +///////////////////////////////////////////////////////////////////////////// +// Enums +enum class D3D11DeviceVTbl : short +{ + // IUnknown + QueryInterface = 0, + AddRef = 1, + Release = 2, + + // ID3D11Device + CreateBuffer = 3, + CreateTexture1D = 4, + CreateTexture2D = 5, + CreateTexture3D = 6, + CreateShaderResourceView = 7, + CreateUnorderedAccessView = 8, + CreateRenderTargetView = 9, + CreateDepthStencilView = 10, + CreateInputLayout = 11, + CreateVertexShader = 12, + CreateGeometryShader = 13, + CreateGeometryShaderWithStreamOutput = 14, + CreatePixelShader = 15, + CreateHullShader = 16, + CreateDomainShader = 17, + CreateComputeShader = 18, + CreateClassLinkage = 19, + CreateBlendState = 20, + CreateDepthStencilState = 21, + CreateRasterizerState = 22, + CreateSamplerState = 23, + CreateQuery = 24, + CreatePredicate = 25, + CreateCounter = 26, + CreateDeferredContext = 27, + OpenSharedResource = 28, + CheckFormatSupport = 29, + CheckMultisampleQualityLevels = 30, + CheckCounterInfo = 31, + CheckCounter = 32, + CheckFeatureSupport = 33, + GetPrivateData = 34, + SetPrivateData = 35, + SetPrivateDataInterface = 36, + GetFeatureLevel = 37, + GetCreationFlags = 38, + GetDeviceRemovedReason = 39, + GetImmediateContext = 40, + SetExceptionMode = 41, + GetExceptionMode = 42, +}; + +enum class DXGISwapChainVTbl : short +{ + // IUnknown + QueryInterface = 0, + AddRef = 1, + Release = 2, + + // IDXGIObject + SetPrivateData = 3, + SetPrivateDataInterface = 4, + GetPrivateData = 5, + GetParent = 6, + + // IDXGIDeviceSubObject + GetDevice = 7, + + // IDXGISwapChain + Present = 8, + GetBuffer = 9, + SetFullscreenState = 10, + GetFullscreenState = 11, + GetDesc = 12, + ResizeBuffers = 13, + ResizeTarget = 14, + GetContainingOutput = 15, + GetFrameStatistics = 16, + GetLastPresentCount = 17, +}; + +class HIDXGI : public IDetour +{ + virtual void debugp(); + /////////////////////////////////////////////////////////////////////////////// +}; +REGISTER(HIDXGI); +#endif // !DEDICATED diff --git a/r5dev/windows/input.cpp b/r5dev/windows/input.cpp new file mode 100644 index 00000000..9998e92f --- /dev/null +++ b/r5dev/windows/input.cpp @@ -0,0 +1,119 @@ +#include "core/stdafx.h" +#include "windows/input.h" + +/*----------------------------------------------------------------------------- + * _input.cpp + *-----------------------------------------------------------------------------*/ + +/////////////////////////////////////////////////////////////////////////////// +typedef BOOL(WINAPI* IGetCursorPos)(LPPOINT lpPoint); +typedef BOOL(WINAPI* ISetCursorPos)(int nX, int nY); +typedef BOOL(WINAPI* IClipCursor)(const RECT* lpRect); +typedef BOOL(WINAPI* IShowCursor)(BOOL bShow); + +/////////////////////////////////////////////////////////////////////////////// +static IGetCursorPos g_oGetCursorPos = nullptr; +static ISetCursorPos g_oSetCursorPos = nullptr; +static IClipCursor g_oClipCursor = nullptr; +static IShowCursor g_oShowCursor = nullptr; + +/////////////////////////////////////////////////////////////////////////////// +static POINT g_pLastCursorPos { 0 }; +extern BOOL g_bBlockInput = false; + +//############################################################################# +// INITIALIZATION +//############################################################################# + +void SetupIPHooks() +{ + g_oSetCursorPos = (ISetCursorPos)DetourFindFunction("user32.dll", "SetCursorPos"); + g_oClipCursor = (IClipCursor )DetourFindFunction("user32.dll", "ClipCursor" ); + g_oGetCursorPos = (IGetCursorPos)DetourFindFunction("user32.dll", "GetCursorPos"); + g_oShowCursor = (IShowCursor )DetourFindFunction("user32.dll", "ShowCursor" ); +} + +//############################################################################# +// INPUT HOOKS +//############################################################################# + +BOOL WINAPI HGetCursorPos(LPPOINT lpPoint) +{ + if (g_bBlockInput) + { + assert(lpPoint != nullptr); + *lpPoint = g_pLastCursorPos; + } + + return g_oGetCursorPos(lpPoint); +} + +BOOL WINAPI HSetCursorPos(int X, int Y) +{ + g_pLastCursorPos.x = X; + g_pLastCursorPos.y = Y; + + if (g_bBlockInput) + { + return TRUE; + } + + return g_oSetCursorPos(X, Y); +} + +BOOL WINAPI HClipCursor(const RECT* lpRect) +{ + if (g_bBlockInput) + { + lpRect = nullptr; + } + + return g_oClipCursor(lpRect); +} + +BOOL WINAPI HShowCursor(BOOL bShow) +{ + if (g_bBlockInput) + { + bShow = TRUE; + } + + return g_oShowCursor(bShow); +} + +//############################################################################# +// MANAGEMENT +//############################################################################# + +void Input_Init() +{ + SetupIPHooks(); + /////////////////////////////////////////////////////////////////////////// + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + /////////////////////////////////////////////////////////////////////////// + DetourAttach(&(LPVOID&)g_oGetCursorPos, (PBYTE)HGetCursorPos); + DetourAttach(&(LPVOID&)g_oSetCursorPos, (PBYTE)HSetCursorPos); + DetourAttach(&(LPVOID&)g_oClipCursor, (PBYTE)HClipCursor); + DetourAttach(&(LPVOID&)g_oShowCursor, (PBYTE)HShowCursor); + + /////////////////////////////////////////////////////////////////////////// + DetourTransactionCommit(); +} + +void Input_Shutdown() +{ + /////////////////////////////////////////////////////////////////////////// + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + /////////////////////////////////////////////////////////////////////////// + DetourDetach(&(LPVOID&)g_oGetCursorPos, (PBYTE)HGetCursorPos); + DetourDetach(&(LPVOID&)g_oSetCursorPos, (PBYTE)HSetCursorPos); + DetourDetach(&(LPVOID&)g_oClipCursor, (PBYTE)HClipCursor); + DetourDetach(&(LPVOID&)g_oShowCursor, (PBYTE)HShowCursor); + + /////////////////////////////////////////////////////////////////////////// + DetourTransactionCommit(); +} diff --git a/r5dev/windows/input.h b/r5dev/windows/input.h new file mode 100644 index 00000000..4c050f86 --- /dev/null +++ b/r5dev/windows/input.h @@ -0,0 +1,12 @@ +#pragma once + +///////////////////////////////////////////////////////////////////////////// +// Internals +void Input_Init(); +void Input_Shutdown(); + +///////////////////////////////////////////////////////////////////////////// +// Globals +extern BOOL g_bBlockInput; + +///////////////////////////////////////////////////////////////////////////// diff --git a/r5launcher/pch.cpp b/r5launcher/pch.cpp deleted file mode 100644 index 17305716..00000000 --- a/r5launcher/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" \ No newline at end of file diff --git a/r5launcher/pch.h b/r5launcher/pch.h deleted file mode 100644 index 021ffd88..00000000 --- a/r5launcher/pch.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#pragma message("[LAUNCHER] pre-compiling headers.\n") - -#include -#include -#include -#include -#include -#include -#include -#include -#include "spdlog.h" diff --git a/r5launcher/r5reloaded.ico b/r5launcher/r5reloaded.ico deleted file mode 100644 index 34370ddf..00000000 Binary files a/r5launcher/r5reloaded.ico and /dev/null differ diff --git a/r5net/include/netpch.h b/r5net/include/netpch.h deleted file mode 100644 index 78f633cf..00000000 --- a/r5net/include/netpch.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#pragma message("Precompiling r5net headers.\n") - -//#define DebugR5Net - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/// - - -#include "httplib.h" -#include "json.hpp" diff --git a/r5net/include/r5/r5net.h b/r5net/include/r5/r5net.h deleted file mode 100644 index ca6b97c7..00000000 --- a/r5net/include/r5/r5net.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "serverlisting.h" - -namespace R5Net -{ - struct Config - { - std::string R_MOTD; - int R_SERVER_TTL; - int R_MIN_REQUIRED_VERSION; - bool loadBanList; - }; - - class Client - { - public: - Client(std::string serverString) : m_HttpClient(serverString.c_str()) - { - m_HttpClient.set_connection_timeout(10); - } - - std::vector GetServersList(std::string& outMessage); - bool PostServerHost(std::string& outMessage, std::string& outToken, const ServerListing& serverListing); - bool GetServerByToken(ServerListing& outServer, std::string& outMessage, const std::string token); - bool GetClientIsBanned(std::string ip, std::int64_t orid, std::string& outErrCl); - std::string GetVersionString(); - - private: - httplib::Client m_HttpClient; - Config config; - }; -} \ No newline at end of file diff --git a/r5net/include/r5/serverlisting.h b/r5net/include/r5/serverlisting.h deleted file mode 100644 index 1e895fbd..00000000 --- a/r5net/include/r5/serverlisting.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -struct ServerListing -{ - std::string name; - std::string map; - std::string ip; - std::string port; - std::string playlist; - bool hidden; - std::string remoteChecksum; - std::string version; - std::string netchanEncryptionKey; -}; - diff --git a/r5net/r5net.vcxproj b/r5net/r5net.vcxproj deleted file mode 100644 index 1ba94683..00000000 --- a/r5net/r5net.vcxproj +++ /dev/null @@ -1,178 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - - - - - - - Create - Create - - - - - - 16.0 - Win32Proj - {f04be619-0326-4ff1-b06b-fbe882e04d5e} - r5net - 10.0 - - - - StaticLibrary - true - v142 - Unicode - - - StaticLibrary - false - v142 - true - Unicode - - - StaticLibrary - true - v142 - Unicode - - - StaticLibrary - false - v142 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - - - false - - - true - $(ProjectDir)lib\$(Configuration)\ - $(SolutionDir)build\$(ProjectName)\$(Configuration)\ - $(SolutionDir)shared\include;$(ProjectDir)include;$(IncludePath) - - - false - $(ProjectDir)lib\$(Configuration)\ - $(SolutionDir)build\$(ProjectName)\$(Configuration)\ - $(SolutionDir)shared\include;$(ProjectDir)include;$(IncludePath) - - - - Level3 - true - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - true - Use - pch.h - - - - - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) - true - Use - pch.h - - - - - true - true - true - - - - - Level3 - true - _DEBUG;_LIB;%(PreprocessorDefinitions) - true - Use - netpch.h - stdcpp17 - MultiThreadedDebug - - - - - true - - - - - Level3 - true - true - true - NDEBUG;_LIB;%(PreprocessorDefinitions) - true - Use - netpch.h - stdcpp17 - - - - - true - true - true - - - - - - \ No newline at end of file diff --git a/r5net/r5net.vcxproj.filters b/r5net/r5net.vcxproj.filters deleted file mode 100644 index 91f23065..00000000 --- a/r5net/r5net.vcxproj.filters +++ /dev/null @@ -1,33 +0,0 @@ - - - - - {676c0758-86e4-4159-8aaf-17969db2d381} - - - {a3728292-4416-44be-a05c-5642a99dbbad} - - - - - include - - - include - - - include - - - - - src - - - src - - - src - - - \ No newline at end of file diff --git a/r5net/src/netpch.cpp b/r5net/src/netpch.cpp deleted file mode 100644 index 0c622c94..00000000 --- a/r5net/src/netpch.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include "netpch.h" \ No newline at end of file diff --git a/r5net/src/r5net.cpp b/r5net/src/r5net.cpp deleted file mode 100644 index 3c007778..00000000 --- a/r5net/src/r5net.cpp +++ /dev/null @@ -1,264 +0,0 @@ -// r5net.cpp : Defines the functions for the static library. -// - -#include "netpch.h" -#include "r5\r5net.h" - -std::string R5Net::Client::GetVersionString() -{ - return "beta 1.6"; -} - -std::vector R5Net::Client::GetServersList(std::string& outMessage) -{ - std::vector list{ }; - - nlohmann::json reqBody = nlohmann::json::object(); - reqBody["version"] = GetVersionString(); - - std::string reqBodyStr = reqBody.dump(); - -#ifdef DebugR5Net - std::cout << " [+R5Net+] Sending GetServerList post now..\n"; -#endif - - httplib::Result res = m_HttpClient.Post("/servers", reqBody.dump().c_str(), reqBody.dump().length(), "application/json"); - -#ifdef DebugR5Net - std::cout << " [+R5Net+] GetServerList replied with " << res->status << "\n"; -#endif - - if (res && res->status == 200) // STATUS_OK - { - nlohmann::json resBody = nlohmann::json::parse(res->body); - if (resBody["success"].is_boolean() && resBody["success"].get()) - { - for (auto obj : resBody["servers"]) - { - list.push_back( - ServerListing{ - obj.value("name",""), - obj.value("map", ""), - obj.value("ip", ""), - obj.value("port", ""), - obj.value("gamemode", ""), - obj.value("hidden", "false") == "true", - obj.value("remote_checksum", ""), - obj.value("version", GetVersionString()), - obj.value("encKey", "") - } - ); - } - } - else - { - if (resBody["err"].is_string()) - outMessage = resBody["err"].get(); - else - outMessage = "An unknown error occured!"; - } - } - else - { - if (res) - { - if (!res->body.empty()) - { - nlohmann::json resBody = nlohmann::json::parse(res->body); - - if (resBody["err"].is_string()) - outMessage = resBody["err"].get(); - else - outMessage = std::string("Failed to reach comp-server ") + std::to_string(res->status); - - return list; - } - - outMessage = std::string("Failed to reach comp-server ") + std::to_string(res->status); - return list; - } - - outMessage = "failed to reach comp-server unknown error code."; - return list; - } - - return list; -} - -bool R5Net::Client::PostServerHost(std::string& outMessage, std::string& outToken, const ServerListing& serverListing) -{ - nlohmann::json reqBody = nlohmann::json::object(); - reqBody["name"] = serverListing.name; - reqBody["map"] = serverListing.map; - reqBody["port"] = serverListing.port; - reqBody["remote_checksum"] = serverListing.remoteChecksum; - reqBody["version"] = GetVersionString(); - reqBody["gamemode"] = serverListing.playlist; - reqBody["encKey"] = serverListing.netchanEncryptionKey; - reqBody["hidden"] = serverListing.hidden; - - std::string reqBodyStr = reqBody.dump(); - - #ifdef DebugR5Net - std::cout << " [+R5Net+] Sending PostServerHost post now..\n" << reqBodyStr << "\n"; -#endif - - httplib::Result res = m_HttpClient.Post("/servers/add", reqBodyStr.c_str(), reqBodyStr.length(), "application/json"); - -#ifdef DebugR5Net - std::cout << " [+R5Net+] PostServerHost replied with " << res->status << "\n"; -#endif - - if (res && res->status == 200) // STATUS_OK - { - nlohmann::json resBody = nlohmann::json::parse(res->body); - if (resBody["success"].is_boolean() && resBody["success"].get()) - { - if (resBody["token"].is_string()) - outToken = resBody["token"].get(); - else - outToken = std::string(); - - return true; - } - else - { - if (resBody["err"].is_string()) - outMessage = resBody["err"].get(); - else - outMessage = "An unknown error occured!"; - - return false; - } - } - else - { - if (res) - { - if (!res->body.empty()) - { - nlohmann::json resBody = nlohmann::json::parse(res->body); - - if (resBody["err"].is_string()) - outMessage = resBody["err"].get(); - else - outMessage = std::string("Failed to reach comp-server ") + std::to_string(res->status); - - outToken = std::string(); - return false; - } - - outToken = std::string(); - outMessage = std::string("Failed to reach comp-server ") + std::to_string(res->status); - return false; - } - - outToken = std::string(); - outMessage = "failed to reach comp-server unknown error code."; - return false; - } - - return false; -} - -bool R5Net::Client::GetServerByToken(ServerListing& outServer, std::string& outMessage, const std::string token) -{ - nlohmann::json reqBody = nlohmann::json::object(); - - reqBody["token"] = token; - -#ifdef DebugR5Net - std::cout << " [+R5Net+] Sending GetServerByToken post now...\n"; -#endif - - httplib::Result res = m_HttpClient.Post("/server/byToken", reqBody.dump().c_str(), reqBody.dump().length(), "application/json"); - -#ifdef DebugR5Net - std::cout << " [+R5Net+] GetServerByToken replied with " << res->status << "\n"; -#endif - - if (res && res->status == 200) // STATUS_OK - { - if (!res->body.empty()) - { - nlohmann::json resBody = nlohmann::json::parse(res->body); - - if (res && resBody["success"].is_boolean() && resBody["success"]) - { - outServer = ServerListing{ - resBody["server"].value("name",""), - resBody["server"].value("map", ""), - resBody["server"].value("ip", ""), - resBody["server"].value("port", ""), - resBody["server"].value("gamemode", ""), - resBody["server"].value("hidden", "false") == "true", - resBody["server"].value("remote_checksum", ""), - resBody["server"].value("version", GetVersionString()), - resBody["server"].value("encKey", "") - }; - return true; - } - else - { - if (resBody["err"].is_string()) - outMessage = resBody["err"].get(); - else - outMessage = ""; - - outServer = ServerListing{}; - return false; - } - } - } - else - { - if (res) - { - if (!res->body.empty()) - { - nlohmann::json resBody = nlohmann::json::parse(res->body); - - if (resBody["err"].is_string()) - outMessage = resBody["err"].get(); - else - outMessage = std::string("Failed to reach comp-server ") + std::to_string(res->status); - - return false; - } - - outMessage = std::string("Failed to reach comp-server ") + std::to_string(res->status); - return false; - } - - outMessage = "failed to reach comp-server unknown error code."; - outServer = ServerListing{}; - return false; - } - - return false; -} - -bool R5Net::Client::GetClientIsBanned(const std::string ip, std::int64_t orid, std::string& outErrCl) -{ - nlohmann::json reqBody = nlohmann::json::object(); - reqBody["ip"] = ip; - reqBody["orid"] = orid; - - httplib::Result res = m_HttpClient.Post("/banlist/isBanned", reqBody.dump().c_str(), reqBody.dump().length(), "application/json"); - - if (res && res->status == 200) - { - nlohmann::json resBody = nlohmann::json::parse(res->body); - - if (resBody["success"].is_boolean() && resBody["success"].get()) - { - if (resBody["isBanned"].is_boolean() && resBody["isBanned"].get()) - { - outErrCl = resBody.value("errCl", "Generic error (code:gen). Contact R5Reloaded developers."); - return true; - } - } - } - return false; -} - diff --git a/r5net/src/serverlisting.cpp b/r5net/src/serverlisting.cpp deleted file mode 100644 index 1e90e1a8..00000000 --- a/r5net/src/serverlisting.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "netpch.h" -#include "r5\serverlisting.h" diff --git a/shared/include/banlist.h b/shared/include/banlist.h deleted file mode 100644 index 3ed13f42..00000000 --- a/shared/include/banlist.h +++ /dev/null @@ -1,155 +0,0 @@ -#pragma once -#include "pch.h" - -class BanList -{ -public: - BanList() - { - Load(); - } - - void operator[](std::pair pair) - { - AddEntry(pair.first, pair.second); - } - - void Load() - { - std::filesystem::path path = std::filesystem::current_path() /= "banlist.config"; // Get current path + banlist.config - - nlohmann::json in; - std::ifstream banFile(path, std::ios::in); // Parse ban list. - - int totalBans = 0; - - if (banFile.good() && banFile) // Check if it parsed. - { - banFile >> in; // into json. - banFile.close(); // Close file. - - if (!in.is_null()) // Check if json is valid - { - if (!in["totalBans"].is_null()) // Is the totalBans field populated? - { - totalBans = in["totalBans"].get(); // Get the totalBans field. - } - } - - for (int i = 0; i < totalBans; i++) // Loop through total bans. - { - nlohmann::json entry = in[std::to_string(i).c_str()]; // Get Entry for current ban. - if (entry.is_null()) // Check if entry is valid. - continue; - - std::int64_t originID = entry["originID"].get(); // Get originID field from entry. - std::string ipAddress = entry["ipAddress"].get(); // Get ipAddress field from entry. - - banList.push_back(std::make_pair(ipAddress, originID)); // Push back into vector. - } - } - } - - void Save() - { - nlohmann::json out; - - for (int i = 0; i < banList.size(); i++) - { - out["totalBans"] = banList.size(); // Populate totalBans field. - out[std::to_string(i).c_str()]["ipAddress"] = banList[i].first; // Populate ipAddress field for this entry. - out[std::to_string(i).c_str()]["originID"] = banList[i].second; // Populate originID field for this entry. - } - - std::filesystem::path path = std::filesystem::current_path() /= "banlist.config"; // Get current path + banlist.config - std::ofstream outFile(path, std::ios::out | std::ios::trunc); // Write config file.. - - outFile << out.dump(4); // Dump it into config file.. - outFile.close(); // Close the file handle. - } - - void AddEntry(std::string ipAddress, std::int64_t originID) - { - if (!ipAddress.empty() && originID > 0) // Check if args are valid. - { - banList.push_back(std::make_pair(ipAddress, originID)); // Push it back into the vector. - } - } - - void DeleteEntry(std::string ipAddress, std::int64_t originID) - { - for (int i = 0; i < banList.size(); i++) // Loop through vector. - { - if (ipAddress.compare(banList[i].first) == NULL || originID == banList[i].second) // Do any entries match our vector? - { - banList.erase(banList.begin() + i); // If so erase that vector element. - } - } - } - - void AddConnectionRefuse(std::string error, int userID) - { - if (refuseList.empty()) - { - refuseList.push_back(std::make_pair(error, userID)); - } - else - { - for (int i = 0; i < refuseList.size(); i++) // Loop through vector. - { - if (refuseList[i].second != userID) // Do any entries match our vector? - { - refuseList.push_back(std::make_pair(error, userID)); // Push it back into the vector. - } - } - } - } - - void DeleteConnectionRefuse(int userID) - { - for (int i = 0; i < refuseList.size(); i++) // Loop through vector. - { - if (refuseList[i].second == userID) // Do any entries match our vector? - { - refuseList.erase(refuseList.begin() + i); // If so erase that vector element. - } - } - } - - bool IsBanned(std::string ipAddress, std::int64_t originID) - { - for (int i = 0; i < banList.size(); i++) - { - std::string ip = banList[i].first; // Get first pair entry. - std::int64_t origin = banList[i].second; // Get second pair entry. - - if (ip.empty()) // Check if ip is empty. - continue; - - if (origin <= 0) // Is originID below 0? - continue; - - if (ip.compare(ipAddress) == NULL) // Do they match? - return true; - - if (originID == origin) // Do they match? - return true; - } - - return false; - } - - bool IsRefuseListValid() - { - return !refuseList.empty(); - } - - bool IsBanListValid() - { - return !banList.empty(); - } - - std::vector> refuseList = {};; -private: - std::vector> banList = {}; -}; diff --git a/shared/include/utility.h b/shared/include/utility.h deleted file mode 100644 index 2f4704db..00000000 --- a/shared/include/utility.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -///////////////////////////////////////////////////////////////////////////// -// Internals -BOOL FileExists(LPCTSTR szPath); -MODULEINFO GetModuleInfo(const char* szModule); - -///////////////////////////////////////////////////////////////////////////// -// Utility -void DbgPrint(LPCSTR sFormat, ...); -void HexDump(const char* szHeader, int nFunc, const void* pData, int nSize); -void PatchNetVarConVar(); - -///////////////////////////////////////////////////////////////////////////// -// Loggers -inline auto g_spddefault_logger = spdlog::basic_logger_mt("default_logger", "platform\\log\\default_r5.log"); -inline auto g_spdnetchan_logger = spdlog::basic_logger_mt("netchan_logger", "platform\\log\\netchan_r5.log"); - -///////////////////////////////////////////////////////////////////////////// - -std::string base64_encode(const std::string& in); -std::string base64_decode(const std::string& in); \ No newline at end of file diff --git a/shared/utility.cpp b/shared/utility.cpp deleted file mode 100644 index 3d6e1f30..00000000 --- a/shared/utility.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include "pch.h" -#include "utility.h" - -/*----------------------------------------------------------------------------- - * _utility.cpp - *-----------------------------------------------------------------------------*/ - - ////////////////////////////////////////////////////////////////////////////// - // -BOOL FileExists(LPCTSTR szPath) -{ - DWORD dwAttrib = GetFileAttributes(szPath); - - return (dwAttrib != INVALID_FILE_ATTRIBUTES && - !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); -} - -/////////////////////////////////////////////////////////////////////////////// -// For getting information about the specified module -MODULEINFO GetModuleInfo(const char* szModule) -{ - MODULEINFO modinfo = { 0 }; - HMODULE hModule = GetModuleHandle(szModule); - if (hModule == 0) - { - return modinfo; - } - GetModuleInformation(GetCurrentProcess(), hModule, &modinfo, sizeof(MODULEINFO)); - return modinfo; -} - -/////////////////////////////////////////////////////////////////////////////// -// -void DbgPrint(LPCSTR sFormat, ...) -{ - CHAR sBuffer[512] = { 0 }; - va_list sArgs; - - // Get the variable arg pointer - va_start(sArgs, sFormat); - - // Format print the string - int length = vsnprintf(sBuffer, sizeof(sBuffer), sFormat, sArgs); - va_end(sArgs); - - // Output the string to the debugger - OutputDebugString(sBuffer); -} - -void PatchNetVarConVar() -{ - CHAR convarPtr[] = "\x72\x3a\x73\x76\x72\x75\x73\x7a\x7a\x03\x04"; - PCHAR curr = convarPtr; - while (*curr) { - *curr ^= 'B'; - ++curr; - } - - std::int64_t cvaraddr = 0; - std::stringstream ss; - ss << std::hex << std::string(convarPtr); - ss >> cvaraddr; - void* cvarptr = reinterpret_cast(cvaraddr); - - if (*reinterpret_cast(cvarptr) == 144) - { - std::uint8_t padding[] = - { - 0x48, 0x8B, 0x45, 0x58, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - void* Callback = nullptr; - VirtualAlloc(Callback, 10, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - memcpy(Callback, (void*)padding, 9); - reinterpret_cast(Callback)(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// For dumping data from a buffer to a file on the disk -void HexDump(const char* szHeader, int nFunc, const void* pData, int nSize) -{ - static std::atomic i, j, k = 0; - static char ascii[17] = { 0 }; - static auto logger = spdlog::get("default_logger"); - auto pattern = std::make_unique("%v", spdlog::pattern_time_type::local, std::string("")); - - // Loop until the function returned to the first caller - while (k == 1) { /*Sleep(75);*/ } - - k = 1; - ascii[16] = '\0'; - - // Add new loggers here to replace the placeholder - if (nFunc == 0) { logger = g_spdnetchan_logger; } - - // Add timestamp - logger->set_level(spdlog::level::trace); - logger->set_pattern("%v [%H:%M:%S.%f]\n"); - logger->trace("---------------------------------------------------------"); - - // Disable EOL and create block header - logger->set_formatter(std::move(pattern)); - logger->trace("{:s} ---- LEN BYTES: {}\n:\n", szHeader, nSize); - logger->trace("-------- 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF\n"); - - // Output the buffer to the file - for (i = 0; i < nSize; i++) - { - if (i % nSize == 0) { logger->trace(" 0x{:04X} ", i); } - logger->trace("{:02x} ", ((unsigned char*)pData)[i]); - - if (((unsigned char*)pData)[i] >= ' ' && ((unsigned char*)pData)[i] <= '~') { ascii[i % 16] = ((unsigned char*)pData)[i]; } - else { ascii[i % 16] = '.'; } - - if ((i + 1) % 8 == 0 || i + 1 == nSize) - { - logger->trace(" "); - - if ((i + 1) % 16 == 0) - { - if (i + 1 == nSize) - { - logger->trace("{:s}\n", ascii); - logger->trace("---------------------------------------------------------------------------\n"); - logger->trace("\n"); - } - else - { - i++; - logger->trace("{:s}\n ", ascii); - logger->trace("0x{:04X} ", i--); - } - } - else if (i + 1 == nSize) - { - ascii[(i + 1) % 16] = '\0'; - if ((i + 1) % 16 <= 8) - { - logger->trace(" "); - } - for (j = (i + 1) % 16; j < 16; j++) - { - logger->trace(" "); - } - logger->trace("{:s}\n", ascii); - logger->trace("---------------------------------------------------------------------------\n"); - logger->trace("\n"); - } - } - } - k = 0; - /////////////////////////////////////////////////////////////////////////// -} - -///// BASE 64 -std::string base64_encode(const std::string& in) { - - std::string out; - - int val = 0, valb = -6; - for (unsigned char c : in) { - val = (val << 8) + c; - valb += 8; - while (valb >= 0) { - out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val >> valb) & 0x3F]); - valb -= 6; - } - } - if (valb > -6) out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val << 8) >> (valb + 8)) & 0x3F]); - while (out.size() % 4) out.push_back('='); - return out; -} - -std::string base64_decode(const std::string& in) { - - std::string out; - - std::vector T(256, -1); - for (int i = 0; i < 64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; - - int val = 0, valb = -8; - for (unsigned char c : in) { - if (T[c] == -1) break; - val = (val << 6) + T[c]; - valb += 6; - if (valb >= 0) { - out.push_back(char((val >> valb) & 0xFF)); - valb -= 8; - } - } - return out; -} \ No newline at end of file