├── sdk ├── c_input.h ├── c_usercmd.h ├── c_globalvars.h ├── c_entity.h ├── matrix.h └── vector3d.h ├── apexinternal.user ├── apexinternal.vcxproj.user ├── README.md ├── apexinternal.sln ├── gui ├── imgui_impl_dx11.h ├── imconfig.h ├── stb_rect_pack.h ├── imgui_impl_dx11.cpp └── stb_textedit.h ├── memory.h ├── apexinternal.filters ├── global.h ├── xorstr.h ├── main.cpp ├── apexinternal.vcxproj └── lazyimporter.h /sdk/c_input.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../global.h" 3 | class c_input { 4 | public: 5 | void third_person(bool enable) { 6 | *(bool*)((uintptr_t)this + 0x8D) = enable; 7 | } 8 | }; c_input* g_pinput = nullptr; -------------------------------------------------------------------------------- /apexinternal.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | 6 | -------------------------------------------------------------------------------- /apexinternal.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | 6 | -------------------------------------------------------------------------------- /sdk/c_usercmd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../global.h" 3 | 4 | class CUserCmd 5 | { 6 | public: 7 | int command_number; 8 | int tick_count; 9 | float command_time; 10 | c_vec viewangles; 11 | char pad[0xC]; 12 | float forwardmove; //clamp to [-1;1] 13 | float sidemove; //clamp to [-1;1] 14 | float upmove; //clamp to [-1;1] 15 | int buttons; 16 | BYTE impulse; 17 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | features 2 | esp: 3 | - box 4 | - chams 5 | 6 | how to use 7 | 1. Enable discord overlay + enable apex in game activity 8 | 2. Disable origin overlay 9 | 3. Inject in apex with injector that supports thread hacking 10 | 4. Press insert in game 11 | 12 | Done with imgui and Direct11 hook with discord overlay 13 | 14 | ImGui repo - https://github.com/ocornut/imgui 15 | 16 | DirectX hook repo - https://github.com/GayPig/DirectX11-hook-with-discord 17 | -------------------------------------------------------------------------------- /sdk/c_globalvars.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../global.h" 3 | 4 | class c_globalvars 5 | { 6 | public: 7 | float realtime; //0x0000 8 | int framecount; //0x0004 9 | float absoluteframetime; //0x0008 10 | float curtime; //0x000C 11 | float frametime; //0x0010 12 | int tickcount; //0x0014 13 | float Unk1; //0x0018 14 | float interval_per_tick; //0x001C 15 | float interpolation_amount; //0x0020 16 | int simTicksThisFrame; //0x0024 17 | int network_protocol; //0x0028 18 | }; c_globalvars* g_pglobals = nullptr; -------------------------------------------------------------------------------- /apexinternal.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.421 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "apexinternal", "apexinternal\apexinternal.vcxproj", "{E5BA3482-8AD3-4589-B8BE-DC9AFFBFE17E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {E5BA3482-8AD3-4589-B8BE-DC9AFFBFE17E}.Debug|x64.ActiveCfg = Debug|x64 17 | {E5BA3482-8AD3-4589-B8BE-DC9AFFBFE17E}.Debug|x64.Build.0 = Debug|x64 18 | {E5BA3482-8AD3-4589-B8BE-DC9AFFBFE17E}.Debug|x86.ActiveCfg = Debug|Win32 19 | {E5BA3482-8AD3-4589-B8BE-DC9AFFBFE17E}.Debug|x86.Build.0 = Debug|Win32 20 | {E5BA3482-8AD3-4589-B8BE-DC9AFFBFE17E}.Release|x64.ActiveCfg = Release|x64 21 | {E5BA3482-8AD3-4589-B8BE-DC9AFFBFE17E}.Release|x64.Build.0 = Release|x64 22 | {E5BA3482-8AD3-4589-B8BE-DC9AFFBFE17E}.Release|x86.ActiveCfg = Release|Win32 23 | {E5BA3482-8AD3-4589-B8BE-DC9AFFBFE17E}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {C0503A60-C8A0-4246-A96B-4852B584ABAB} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /gui/imgui_impl_dx11.h: -------------------------------------------------------------------------------- 1 | // ImGui Win32 + DirectX11 binding 2 | // In this binding, ImTextureID is used to store a 'ID3D11ShaderResourceView*' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. 3 | 4 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 5 | // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). 6 | // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. 7 | // https://github.com/ocornut/imgui 8 | #include 9 | 10 | struct ID3D11Device; 11 | struct ID3D11DeviceContext; 12 | 13 | IMGUI_API bool ImGui_ImplDX11_Init(void* hwnd, ID3D11Device* device, ID3D11DeviceContext* device_context); 14 | IMGUI_API void ImGui_ImplDX11_Shutdown(); 15 | IMGUI_API void ImGui_ImplDX11_NewFrame(); 16 | 17 | // Use if you want to reset your rendering device without losing ImGui state. 18 | IMGUI_API void ImGui_ImplDX11_InvalidateDeviceObjects(); 19 | IMGUI_API bool ImGui_ImplDX11_CreateDeviceObjects(); 20 | IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); 21 | 22 | // Handler for Win32 messages, update mouse/keyboard data. 23 | // You may or not need this for your implementation, but it can serve as reference for handling inputs. 24 | // Commented out to avoid dragging dependencies on types. You can copy the extern declaration in your code. 25 | /* 26 | IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 27 | */ 28 | -------------------------------------------------------------------------------- /memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "global.h" 3 | 4 | 5 | namespace memory { 6 | inline uintptr_t occurence(const char* module, const char* pattern) { 7 | 8 | #define in_range(x, a, b) (x >= a && x <= b) 9 | #define get_bits(x) (in_range((x & (~0x20)), 'A', 'F') ? ((x & (~0x20)) - 'A' + 0xA): (in_range(x, '0', '9') ? x - '0': 0)) 10 | #define get_byte(x) (get_bits(x[0]) << 4 | get_bits(x[1])) 11 | 12 | MODULEINFO mod; 13 | iat(K32GetModuleInformation).get()(iat(GetCurrentProcess).get()(), iat(GetModuleHandleA).get()(module), &mod, sizeof(MODULEINFO)); 14 | uintptr_t start = (uintptr_t)mod.lpBaseOfDll; 15 | uintptr_t end = (uintptr_t)mod.lpBaseOfDll + (uintptr_t)mod.SizeOfImage; 16 | uintptr_t match = 0; 17 | const char* current = pattern; 18 | 19 | for (uintptr_t pCur = start; pCur < end; pCur++) { 20 | 21 | if (!*current) 22 | return match; 23 | 24 | if (*(PBYTE)current == ('\?') || *(BYTE*)pCur == get_byte(current)) { 25 | if (!match) 26 | match = pCur; 27 | 28 | if (!current[2]) 29 | return match; 30 | 31 | if (*(PWORD)current == ('\?\?') || *(PBYTE)current != ('\?')) 32 | current += 3; 33 | else 34 | current += 2; 35 | } else { 36 | current = pattern; 37 | match = 0; 38 | } 39 | } 40 | return 0; 41 | } 42 | 43 | inline uintptr_t dereference(uintptr_t address, unsigned int offset) 44 | { 45 | if (address == 0) 46 | return 0; 47 | 48 | if (sizeof(uintptr_t) == 8) 49 | return address + (int)((*(int*)(address + offset) + offset) + sizeof(int)); 50 | 51 | return (uintptr_t)*(unsigned long*)(address + offset); 52 | 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /sdk/c_entity.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../global.h" 3 | 4 | enum 5 | { 6 | LIFE_INVALID, 7 | LIFE_DISCARDBODY, 8 | LIFE_DEAD, 9 | LIFE_DYING, 10 | LIFE_ALIVE 11 | }; 12 | 13 | class c_weapon { 14 | public: 15 | 16 | }; 17 | 18 | class c_entity 19 | { 20 | public: 21 | inline int m_iHealth() { 22 | return *(int*)((uintptr_t)this + 0x3D4); 23 | } 24 | 25 | inline int m_iShield() { 26 | return *(int*)((uintptr_t)this + 0x150); 27 | } 28 | 29 | inline int m_iLifeState() { 30 | return *(int*)((uintptr_t)this + 0x718); 31 | } 32 | 33 | inline int m_iId() { 34 | return *(int*)((uintptr_t)this + 0x8); 35 | } 36 | 37 | inline int m_iTeam() { 38 | return *(int*)((uintptr_t)this + 0x3E4); 39 | } 40 | 41 | inline void hl_make_glow() { 42 | *(bool*)((uintptr_t)this + 0x380) = true; 43 | *(int*)((uintptr_t)this + 0x2F0) = 1; 44 | *(float*)((uintptr_t)this + 0x2DC) = FLT_MAX; 45 | for (int offset = 688; offset <= 712; offset += 4) 46 | *(float*)(this + offset) = FLT_MAX; 47 | 48 | *(c_vec*)((uintptr_t)this + 0x1B0) = c_vec(11,250,255); 49 | *(float*)((uintptr_t)this + 0x1C0) = 35; 50 | } 51 | 52 | inline char* m_sName() { 53 | typedef char*(__fastcall *t_get_entity_name)(uintptr_t entity_pointer); 54 | static t_get_entity_name fn_get_entity_name = (t_get_entity_name)(dwbase + 0x6E72B0); 55 | return fn_get_entity_name((uintptr_t)this); 56 | } 57 | 58 | inline char* m_hHandle() { 59 | return *(char**)((uintptr_t)this + 0x500); 60 | } 61 | 62 | inline c_vec m_vPos() { 63 | return *(c_vec*)((uintptr_t)this + 0x12C); 64 | } 65 | inline c_vec m_vAngles() { 66 | return *(c_vec*)((uintptr_t)this + 0x20BC); 67 | } 68 | 69 | inline c_weapon* m_ActiveWeapon() { 70 | return *(c_weapon**)((((uintptr_t)this + 0x1634) & 0xFFFF) << 5); 71 | } 72 | 73 | inline c_vec m_vEyes() { 74 | return *(c_vec*)((uintptr_t)this + 0x3AA0); 75 | } 76 | }; -------------------------------------------------------------------------------- /sdk/matrix.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../global.h" 3 | class matrix3x4_t { 4 | public: 5 | matrix3x4_t() {} 6 | matrix3x4_t( 7 | float m00, float m01, float m02, float m03, 8 | float m10, float m11, float m12, float m13, 9 | float m20, float m21, float m22, float m23) { 10 | m_flMatVal[0][0] = m00; m_flMatVal[0][1] = m01; m_flMatVal[0][2] = m02; m_flMatVal[0][3] = m03; 11 | m_flMatVal[1][0] = m10; m_flMatVal[1][1] = m11; m_flMatVal[1][2] = m12; m_flMatVal[1][3] = m13; 12 | m_flMatVal[2][0] = m20; m_flMatVal[2][1] = m21; m_flMatVal[2][2] = m22; m_flMatVal[2][3] = m23; 13 | } 14 | //----------------------------------------------------------------------------- 15 | // Creates a matrix where the X axis = forward 16 | // the Y axis = left, and the Z axis = up 17 | //----------------------------------------------------------------------------- 18 | void Init(const c_vec& xAxis, const c_vec& yAxis, const c_vec& zAxis, const c_vec &vecOrigin) { 19 | m_flMatVal[0][0] = xAxis.x; m_flMatVal[0][1] = yAxis.x; m_flMatVal[0][2] = zAxis.x; m_flMatVal[0][3] = vecOrigin.x; 20 | m_flMatVal[1][0] = xAxis.y; m_flMatVal[1][1] = yAxis.y; m_flMatVal[1][2] = zAxis.y; m_flMatVal[1][3] = vecOrigin.y; 21 | m_flMatVal[2][0] = xAxis.z; m_flMatVal[2][1] = yAxis.z; m_flMatVal[2][2] = zAxis.z; m_flMatVal[2][3] = vecOrigin.z; 22 | } 23 | 24 | //----------------------------------------------------------------------------- 25 | // Creates a matrix where the X axis = forward 26 | // the Y axis = left, and the Z axis = up 27 | //----------------------------------------------------------------------------- 28 | matrix3x4_t(const c_vec& xAxis, const c_vec& yAxis, const c_vec& zAxis, const c_vec &vecOrigin) { 29 | Init(xAxis, yAxis, zAxis, vecOrigin); 30 | } 31 | 32 | inline void SetOrigin(c_vec const & p) { 33 | m_flMatVal[0][3] = p.x; 34 | m_flMatVal[1][3] = p.y; 35 | m_flMatVal[2][3] = p.z; 36 | } 37 | 38 | inline void Invalidate(void) { 39 | for (int i = 0; i < 3; i++) 40 | { 41 | for (int j = 0; j < 4; j++) 42 | { 43 | m_flMatVal[i][j] = std::numeric_limits::infinity();; 44 | } 45 | } 46 | } 47 | 48 | float *operator[](int i) { return m_flMatVal[i]; } 49 | const float *operator[](int i) const { return m_flMatVal[i]; } 50 | float *Base() { return &m_flMatVal[0][0]; } 51 | const float *Base() const { return &m_flMatVal[0][0]; } 52 | 53 | float m_flMatVal[3][4]; 54 | }; -------------------------------------------------------------------------------- /gui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // USER IMPLEMENTATION 3 | // This file contains compile-time options for ImGui. 4 | // Other options (memory allocation overrides, callbacks, etc.) can be set at runtime via the ImGuiIO structure - ImGui::GetIO(). 5 | //----------------------------------------------------------------------------- 6 | 7 | #pragma once 8 | 9 | //---- Define assertion handler. Defaults to calling assert(). 10 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 11 | 12 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows. 13 | //#define IMGUI_API __declspec( dllexport ) 14 | //#define IMGUI_API __declspec( dllimport ) 15 | 16 | //---- Don't define obsolete functions names. Consider enabling from time to time or when updating to reduce like hood of using already obsolete function/names 17 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 18 | 19 | //---- Include imgui_user.h at the end of imgui.h 20 | //#define IMGUI_INCLUDE_IMGUI_USER_H 21 | 22 | //---- Don't implement default handlers for Windows (so as not to link with OpenClipboard() and others Win32 functions) 23 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS 24 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS 25 | 26 | //---- Don't implement demo windows functionality (ShowDemoWindow()/ShowStyleEditor()/ShowUserGuide() methods will be empty) 27 | //---- It is very strongly recommended to NOT disable the demo windows. Please read the comment at the top of imgui_demo.cpp to learn why. 28 | //#define IMGUI_DISABLE_DEMO_WINDOWS 29 | 30 | //---- Don't implement ImFormatString(), ImFormatStringV() so you can reimplement them yourself. 31 | //#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS 32 | 33 | //---- Pack colors to BGRA instead of RGBA (remove need to post process vertex buffer in back ends) 34 | //#define IMGUI_USE_BGRA_PACKED_COLOR 35 | 36 | //---- Implement STB libraries in a namespace to avoid linkage conflicts 37 | //#define IMGUI_STB_NAMESPACE ImGuiStb 38 | 39 | //---- Define constructor and implicit cast operators to convert back<>forth from your math types and ImVec2/ImVec4. 40 | /* 41 | #define IM_VEC2_CLASS_EXTRA \ 42 | ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ 43 | operator MyVec2() const { return MyVec2(x,y); } 44 | 45 | #define IM_VEC4_CLASS_EXTRA \ 46 | ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ 47 | operator MyVec4() const { return MyVec4(x,y,z,w); } 48 | */ 49 | 50 | //---- Use 32-bit vertex indices (instead of default: 16-bit) to allow meshes with more than 64K vertices 51 | //#define ImDrawIdx unsigned int 52 | 53 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. 54 | //---- e.g. create variants of the ImGui::Value() helper for your low-level math types, or your own widgets/helpers. 55 | /* 56 | namespace ImGui 57 | { 58 | void Value(const char* prefix, const MyMatrix44& v, const char* float_format = NULL); 59 | } 60 | */ 61 | 62 | -------------------------------------------------------------------------------- /apexinternal.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | Header Files 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files 58 | 59 | 60 | Header Files 61 | 62 | 63 | Header Files 64 | 65 | 66 | Header Files 67 | 68 | 69 | Header Files 70 | 71 | 72 | Header Files 73 | 74 | 75 | Header Files 76 | 77 | 78 | Header Files 79 | 80 | 81 | Header Files 82 | 83 | 84 | Header Files 85 | 86 | 87 | -------------------------------------------------------------------------------- /sdk/vector3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class c_vec { 4 | public: 5 | float x, y, z; 6 | 7 | inline c_vec() { 8 | this->x = 0; 9 | this->y = 0; 10 | this->z = 0; 11 | } 12 | 13 | inline c_vec(float x, float y, float z) { 14 | this->x = x; 15 | this->y = y; 16 | this->z = z; 17 | } 18 | 19 | inline c_vec &operator=(const c_vec &vOther) 20 | { 21 | x = vOther.x; 22 | y = vOther.y; 23 | z = vOther.z; 24 | return *this; 25 | } 26 | 27 | inline float &operator[](int i) 28 | { 29 | return ((float*)this)[i]; 30 | } 31 | 32 | inline float operator[](int i) const 33 | { 34 | return ((float*)this)[i]; 35 | } 36 | 37 | inline bool operator==(const c_vec &src) const 38 | { 39 | return (src.x == x) && (src.y == y) && (src.z == z); 40 | } 41 | 42 | inline bool operator!=(const c_vec &src) const 43 | { 44 | return (src.x != x) || (src.y != y) || (src.z != z); 45 | } 46 | 47 | inline c_vec &operator+=(const c_vec &v) 48 | { 49 | x += v.x; 50 | y += v.y; 51 | z += v.z; 52 | return *this; 53 | } 54 | 55 | inline c_vec &operator-=(const c_vec &v) 56 | { 57 | x -= v.x; 58 | y -= v.y; 59 | z -= v.z; 60 | return *this; 61 | } 62 | 63 | inline c_vec &operator*=(float fl) 64 | { 65 | x *= fl; 66 | y *= fl; 67 | z *= fl; 68 | return *this; 69 | } 70 | 71 | inline c_vec &operator*=(const c_vec &v) 72 | { 73 | x *= v.x; 74 | y *= v.y; 75 | z *= v.z; 76 | return *this; 77 | } 78 | 79 | inline c_vec &operator+=(float fl) 80 | { 81 | x += fl; 82 | y += fl; 83 | z += fl; 84 | return *this; 85 | } 86 | //=============================================== 87 | inline c_vec &operator-=(float fl) 88 | { 89 | x -= fl; 90 | y -= fl; 91 | z -= fl; 92 | return *this; 93 | } 94 | //=============================================== 95 | inline c_vec &operator/=(float fl) 96 | { 97 | float oofl = 1.0f / fl; 98 | x *= oofl; 99 | y *= oofl; 100 | z *= oofl; 101 | return *this; 102 | } 103 | 104 | inline c_vec &operator/=(const c_vec &v) 105 | { 106 | x /= v.x; 107 | y /= v.y; 108 | z /= v.z; 109 | return *this; 110 | } 111 | 112 | inline c_vec operator+(const c_vec &v) const 113 | { 114 | c_vec res; 115 | res.x = x + v.x; 116 | res.y = y + v.y; 117 | res.z = z + v.z; 118 | return res; 119 | } 120 | 121 | 122 | inline c_vec operator-(const c_vec &v) const 123 | { 124 | c_vec res; 125 | res.x = x - v.x; 126 | res.y = y - v.y; 127 | res.z = z - v.z; 128 | return res; 129 | } 130 | 131 | inline c_vec operator*(float fl) const 132 | { 133 | c_vec res; 134 | res.x = x * fl; 135 | res.y = y * fl; 136 | res.z = z * fl; 137 | return res; 138 | } 139 | 140 | inline c_vec operator*(const c_vec &v) const 141 | { 142 | c_vec res; 143 | res.x = x * v.x; 144 | res.y = y * v.y; 145 | res.z = z * v.z; 146 | return res; 147 | } 148 | 149 | inline c_vec operator/(float fl) const 150 | { 151 | c_vec res; 152 | res.x = x / fl; 153 | res.y = y / fl; 154 | res.z = z / fl; 155 | return res; 156 | } 157 | 158 | inline c_vec operator/(const c_vec &v) const 159 | { 160 | c_vec res; 161 | res.x = x / v.x; 162 | res.y = y / v.y; 163 | res.z = z / v.z; 164 | return res; 165 | } 166 | }; -------------------------------------------------------------------------------- /global.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define _CRT_SECURE_NO_WARNINGS 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #pragma comment (lib, "d3d11.lib") 16 | #pragma comment (lib, "d3dx11.lib") 17 | #pragma comment (lib, "d3dx10.lib") 18 | 19 | uintptr_t dwbase = 0; 20 | uintptr_t dwdiscord = 0; 21 | 22 | #include "xorstr.h" 23 | #include "lazyimporter.h" 24 | #include "memory.h" 25 | #include "gui/imgui.h" 26 | #include "gui/imgui_internal.h" 27 | #include "gui/imgui_impl_dx11.h" 28 | #include "sdk/vector3d.h" 29 | #include "sdk/c_globalvars.h" 30 | #include "sdk/c_entity.h" 31 | #include "sdk/matrix.h" 32 | #include "sdk/c_input.h" 33 | 34 | 35 | 36 | typedef int(__stdcall* createhook_fn)(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal); 37 | createhook_fn CreateHook = nullptr; 38 | 39 | typedef int(__stdcall* enablehook_fn)(LPVOID pTarget,BOOL enable); 40 | enablehook_fn EnableHook = nullptr; 41 | 42 | typedef int (__stdcall* applyqueued_fn)(VOID); 43 | applyqueued_fn EnableHookQue = nullptr; 44 | 45 | typedef long(__stdcall *present_fn) (IDXGISwapChain* p_swapchain, UINT syncintreval, UINT flags); 46 | present_fn o_present = nullptr; 47 | 48 | typedef bool(__fastcall* createmove_fn)(void* cInputPtr, int sequence_number, float input_sample_frametime, bool active); 49 | createmove_fn o_createmove = nullptr; 50 | 51 | typedef uintptr_t(__fastcall* createinterface_fn)(const char *, uintptr_t); 52 | createinterface_fn createinterface; 53 | 54 | typedef SHORT(__stdcall* getasynckeystate_fn)(int vKey); 55 | getasynckeystate_fn o_getasynckeystate; 56 | 57 | typedef bool(__fastcall* worldtoscreen_fn)(c_vec& origin, c_vec& screen); 58 | worldtoscreen_fn o_worldtoscreen; 59 | 60 | template 61 | inline t vfunc(DWORD_PTR* pTable, int index) 62 | { 63 | DWORD_PTR* VTableFunctionBase = *(DWORD_PTR**)pTable; 64 | DWORD_PTR dwAddress = VTableFunctionBase[index]; 65 | return (t)(dwAddress); 66 | } 67 | 68 | //trace 69 | #define CONTENTS_EMPTY 0x0 70 | #define CONTENTS_SOLID 0x1 71 | #define CONTENTS_WINDOW 0x2 72 | #define CONTENTS_AUX 0x4 73 | #define CONTENTS_GRATE 0x8 74 | #define CONTENTS_SLIME 0x10 75 | #define CONTENTS_WATER 0x20 76 | #define CONTENTS_WINDOW_NOCOLLIDE 0x40 77 | #define CONTENTS_OPAQUE 0x80 78 | #define CONTENTS_TESTFOGVOLUME 0x100 79 | #define CONTENTS_PHYSICSCLIP 0x200 80 | #define CONTENTS_SOUNDTRIGGER 0x400 81 | #define CONTENTS_NOGRAPPLE 0x800 82 | #define CONTENTS_OCCLUDESOUND 0x1000 83 | #define CONTENTS_IGNORE_NODRAW_OPAQUE 0x2000 84 | #define CONTENTS_MOVEABLE 0x4000 85 | #define CONTENTS_TEST_SOLID_BODY_SHOT 0x8000 86 | #define CONTENTS_PLAYERCLIP 0x10000 87 | #define CONTENTS_MONSTERCLIP 0x20000 88 | #define CONTENTS_OPERATOR_FLOOR 0x40000 89 | #define CONTENTS_BLOCKLOS 0x80000 90 | #define CONTENTS_NOCLIMB 0x100000 91 | #define CONTENTS_TITANCLIP 0x200000 92 | #define CONTENTS_BULLETCLIP 0x400000 93 | #define CONTENTS_OPERATORCLIP 0x800000 94 | #define CONTENTS_NOAIRDROP 0x1000000 95 | #define CONTENTS_MONSTER 0x2000000 96 | #define CONTENTS_DEBRIS 0x4000000 97 | #define CONTENTS_DETAIL 0x8000000 98 | #define CONTENTS_TRANSLUCENT 0x10000000 99 | #define CONTENTS_HITBOX 0x40000000 100 | 101 | #define TRACE_CONTENTS_OPERATOR_FLOOR (CONTENTS_OPERATOR_FLOOR) 102 | #define TRACE_MASK_SOLID (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_PHYSICSCLIP|CONTENTS_MOVEABLE|CONTENTS_MONSTER) 103 | #define TRACE_MASK_PLAYERSOLID (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_MOVEABLE|CONTENTS_PLAYERCLIP|CONTENTS_MONSTER) 104 | #define TRACE_MASK_TITANSOLID (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_MOVEABLE|CONTENTS_TITANCLIP|CONTENTS_MONSTER) 105 | #define TRACE_MASK_NPCSOLID (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_PHYSICSCLIP|CONTENTS_MOVEABLE|CONTENTS_MONSTERCLIP|CONTENTS_MONSTER) 106 | #define TRACE_MASK_NPCFLUID (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_MOVEABLE|CONTENTS_MONSTERCLIP|CONTENTS_MONSTER) 107 | #define TRACE_MASK_SOLID_BRUSHONLY (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_PHYSICSCLIP|CONTENTS_MOVEABLE) 108 | #define TRACE_MASK_PLAYERSOLID_BRUSHONLY (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_MOVEABLE|CONTENTS_PLAYERCLIP) 109 | #define TRACE_MASK_NPCSOLID_BRUSHONLY (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_MOVEABLE|CONTENTS_MONSTERCLIP) 110 | #define TRACE_MASK_WATER (CONTENTS_SLIME|CONTENTS_WATER) 111 | #define TRACE_MASK_OPAQUE (CONTENTS_SOLID|CONTENTS_OPAQUE|CONTENTS_MOVEABLE) 112 | #define TRACE_MASK_OPAQUE_AND_NPCS (CONTENTS_SOLID|CONTENTS_OPAQUE|CONTENTS_MOVEABLE|CONTENTS_MONSTER) 113 | #define TRACE_MASK_BLOCKLOS (CONTENTS_SOLID|CONTENTS_OPAQUE|CONTENTS_MOVEABLE|CONTENTS_BLOCKLOS) 114 | #define TRACE_MASK_BLOCKLOS_AND_NPCS (CONTENTS_SOLID|CONTENTS_OPAQUE|CONTENTS_MOVEABLE|CONTENTS_BLOCKLOS|CONTENTS_MONSTER) 115 | #define TRACE_MASK_VISIBLE (CONTENTS_SOLID|CONTENTS_OPAQUE|CONTENTS_IGNORE_NODRAW_OPAQUE|CONTENTS_MOVEABLE) 116 | #define TRACE_MASK_VISIBLE_AND_NPCS (CONTENTS_SOLID|CONTENTS_OPAQUE|CONTENTS_IGNORE_NODRAW_OPAQUE|CONTENTS_MOVEABLE|CONTENTS_MONSTER) 117 | #define TRACE_MASK_SHOT (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_SLIME|CONTENTS_WATER|CONTENTS_MOVEABLE|CONTENTS_BULLETCLIP|CONTENTS_MONSTER|CONTENTS_DEBRIS|CONTENTS_HITBOX) 118 | #define TRACE_MASK_SHOT_BRUSHONLY (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_SLIME|CONTENTS_WATER|CONTENTS_MOVEABLE|CONTENTS_BULLETCLIP|CONTENTS_DEBRIS) 119 | #define TRACE_MASK_GRENADE (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_MOVEABLE|CONTENTS_BULLETCLIP|CONTENTS_MONSTER|CONTENTS_DEBRIS|CONTENTS_HITBOX) 120 | #define TRACE_MASK_SHOT_HULL (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_MOVEABLE|CONTENTS_BULLETCLIP|CONTENTS_MONSTER|CONTENTS_DEBRIS) 121 | #define TRACE_MASK_NPCWORLDSTATIC (CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_MONSTERCLIP) 122 | 123 | struct surface_t 124 | { 125 | const char* name; 126 | char surface_props; 127 | unsigned short flags; // BUGBUG: These are declared per surface, not per material, but this database is per-material now 128 | }; 129 | 130 | struct plane_t 131 | { 132 | c_vec normal; 133 | float dist; 134 | char type; // for fast side tests 135 | char signbits; // signx + (signy<<1) + (signz<<1) 136 | char pad[2]; 137 | }; 138 | 139 | struct trace_t 140 | { 141 | c_vec startpos; 142 | c_vec endpos; 143 | plane_t plane; 144 | 145 | int unk01; 146 | float fraction; 147 | 148 | int contents; 149 | 150 | bool allsolid; 151 | bool startsolid; 152 | 153 | int unk02; 154 | surface_t surface; 155 | float fractionleftsolid; 156 | int hitgroup; // not sure 157 | short physicsbone; // not sure 158 | unsigned short world_surface_index; // not sure 159 | void* entity; 160 | int hitbox; // not sure 161 | // has to have more here. sizeof trace_t == 0x180 162 | }; 163 | 164 | enum Hitbox 165 | { 166 | NECK_B = 0, // basically head 167 | NECK_A, 168 | SPINE_C, 169 | SPINE_B, 170 | SPINE_A, 171 | HIP, 172 | LEFT_SHOULDER, 173 | LEFT_ELBOW, 174 | LEFT_WRIST, 175 | RIGHT_SHOULDER, 176 | RIGHT_ELBOW, 177 | RIGHT_WRIST, 178 | LEFT_THIGH, 179 | LEFT_KNEE, 180 | LEFT_ANKLE, 181 | LEFT_BALL, 182 | RIGHT_THIGH, 183 | RIGHT_KNEE, 184 | RIGHT_ANKLE, 185 | RIGHT_BALL, 186 | LEFT_THIGH2, 187 | RIGHT_THIGH2, 188 | RIGHT_SHOULDER2, 189 | LEFT_SHOULDER2 190 | }; 191 | 192 | // used for ray tracing 193 | enum Hitgroup 194 | { 195 | GENERIC = 0, 196 | HEAD = 1, 197 | CHEST = 2, 198 | STOMACH = 3, 199 | LEFTARM = 4, 200 | RIGHTARM = 5, 201 | LEFTLEG = 6, 202 | RIGHTLEG = 7, 203 | GEAR = 10 204 | }; -------------------------------------------------------------------------------- /xorstr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef JM_XORSTR_HPP 3 | #define JM_XORSTR_HPP 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define xorstr(str) \ 11 | ::jm::make_xorstr( \ 12 | []() { return str; }, \ 13 | std::make_index_sequence{}, \ 14 | std::make_index_sequence<::jm::detail::_buffer_size()>{}) 15 | #define xorstr_(str) xorstr(str).crypt_get() 16 | 17 | #ifdef _MSC_VER 18 | #define XORSTR_FORCEINLINE __forceinline 19 | #else 20 | #define XORSTR_FORCEINLINE __attribute__((always_inline)) 21 | #endif 22 | 23 | // you can define this macro to get possibly faster code on gcc/clang 24 | // at the expense of constants being put into data section. 25 | #if !defined(XORSTR_ALLOW_DATA) 26 | // MSVC - no volatile 27 | // GCC and clang - volatile everywhere 28 | #if defined(__clang__) || defined(__GNUC__) 29 | #define XORSTR_VOLATILE volatile 30 | #endif 31 | 32 | #endif 33 | #ifndef XORSTR_VOLATILE 34 | #define XORSTR_VOLATILE 35 | #endif 36 | 37 | namespace jm { 38 | 39 | namespace detail { 40 | 41 | template 42 | struct unsigned_; 43 | 44 | template<> 45 | struct unsigned_<1> { 46 | using type = std::uint8_t; 47 | }; 48 | template<> 49 | struct unsigned_<2> { 50 | using type = std::uint16_t; 51 | }; 52 | template<> 53 | struct unsigned_<4> { 54 | using type = std::uint32_t; 55 | }; 56 | 57 | template 58 | struct pack_value_type { 59 | using type = decltype(C); 60 | }; 61 | 62 | template 63 | constexpr std::size_t _buffer_size() 64 | { 65 | return ((Size / 16) + (Size % 16 != 0)) * 2; 66 | } 67 | 68 | template 69 | struct tstring_ { 70 | using value_type = typename pack_value_type::type; 71 | constexpr static std::size_t size = sizeof...(Cs); 72 | constexpr static value_type str[size] = { Cs... }; 73 | 74 | constexpr static std::size_t buffer_size = _buffer_size(); 75 | constexpr static std::size_t buffer_align = 76 | #ifndef JM_XORSTR_DISABLE_AVX_INTRINSICS 77 | ((sizeof(str) > 16) ? 32 : 16); 78 | #else 79 | 16; 80 | #endif 81 | }; 82 | 83 | template 84 | struct _ki { 85 | constexpr static std::size_t idx = I; 86 | constexpr static std::uint64_t key = K; 87 | }; 88 | 89 | template 90 | constexpr std::uint32_t key4() noexcept 91 | { 92 | std::uint32_t value = Seed; 93 | for (char c : __TIME__) 94 | value = static_cast((value ^ c) * 16777619ull); 95 | return value; 96 | } 97 | 98 | template 99 | constexpr std::uint64_t key8() 100 | { 101 | constexpr auto first_part = key4<2166136261 + S>(); 102 | constexpr auto second_part = key4(); 103 | return (static_cast(first_part) << 32) | second_part; 104 | } 105 | 106 | // clang and gcc try really hard to place the constants in data 107 | // sections. to counter that there was a need to create an intermediate 108 | // constexpr string and then copy it into a non constexpr container with 109 | // volatile storage so that the constants would be placed directly into 110 | // code. 111 | template 112 | struct string_storage { 113 | std::uint64_t storage[T::buffer_size]; 114 | 115 | XORSTR_FORCEINLINE constexpr string_storage() noexcept : storage{ Keys... } 116 | { 117 | using cast_type = 118 | typename unsigned_::type; 119 | constexpr auto value_size = sizeof(typename T::value_type); 120 | // puts the string into 64 bit integer blocks in a constexpr 121 | // fashion 122 | for (std::size_t i = 0; i < T::size; ++i) 123 | storage[i / (8 / value_size)] ^= 124 | (std::uint64_t{ static_cast(T::str[i]) } 125 | << ((i % (8 / value_size)) * 8 * value_size)); 126 | } 127 | }; 128 | 129 | } // namespace detail 130 | 131 | template 132 | class xor_string { 133 | alignas(T::buffer_align) std::uint64_t _storage[T::buffer_size]; 134 | 135 | // _single functions needed because MSVC crashes without them 136 | XORSTR_FORCEINLINE void _crypt_256_single(const std::uint64_t* keys, 137 | std::uint64_t* storage) noexcept 138 | 139 | { 140 | _mm256_store_si256( 141 | reinterpret_cast<__m256i*>(storage), 142 | _mm256_xor_si256( 143 | _mm256_load_si256(reinterpret_cast(storage)), 144 | _mm256_load_si256(reinterpret_cast(keys)))); 145 | } 146 | 147 | template 148 | XORSTR_FORCEINLINE void _crypt_256(const std::uint64_t* keys, 149 | std::index_sequence) noexcept 150 | { 151 | (_crypt_256_single(keys + Idxs * 4, _storage + Idxs * 4), ...); 152 | } 153 | 154 | XORSTR_FORCEINLINE void _crypt_128_single(const std::uint64_t* keys, 155 | std::uint64_t* storage) noexcept 156 | { 157 | _mm_store_si128( 158 | reinterpret_cast<__m128i*>(storage), 159 | _mm_xor_si128(_mm_load_si128(reinterpret_cast(storage)), 160 | _mm_load_si128(reinterpret_cast(keys)))); 161 | } 162 | 163 | template 164 | XORSTR_FORCEINLINE void _crypt_128(const std::uint64_t* keys, 165 | std::index_sequence) noexcept 166 | { 167 | (_crypt_128_single(keys + Idxs * 2, _storage + Idxs * 2), ...); 168 | } 169 | 170 | // loop generates vectorized code which places constants in data dir 171 | XORSTR_FORCEINLINE constexpr void _copy() noexcept 172 | { 173 | constexpr detail::string_storage storage; 174 | static_cast(std::initializer_list{ 175 | (const_cast(_storage))[Keys::idx] = 176 | storage.storage[Keys::idx]... }); 177 | } 178 | 179 | public: 180 | using value_type = typename T::value_type; 181 | using size_type = std::size_t; 182 | using pointer = value_type * ; 183 | using const_pointer = const pointer; 184 | 185 | XORSTR_FORCEINLINE xor_string() noexcept { _copy(); } 186 | 187 | XORSTR_FORCEINLINE constexpr size_type size() const noexcept 188 | { 189 | return T::size - 1; 190 | } 191 | 192 | XORSTR_FORCEINLINE void crypt() noexcept 193 | { 194 | alignas(T::buffer_align) std::uint64_t keys[T::buffer_size]; 195 | static_cast(std::initializer_list{ 196 | (const_cast(keys))[Keys::idx] = 197 | Keys::key... }); 198 | 199 | _copy(); 200 | 201 | #ifndef JM_XORSTR_DISABLE_AVX_INTRINSICS 202 | _crypt_256(keys, std::make_index_sequence{}); 203 | if constexpr (T::buffer_size % 4 != 0) 204 | _crypt_128(keys, std::index_sequence{}); 205 | #else 206 | _crypt_128(keys, std::make_index_sequence{}); 207 | #endif 208 | } 209 | 210 | XORSTR_FORCEINLINE const_pointer get() const noexcept 211 | { 212 | return reinterpret_cast(_storage); 213 | } 214 | 215 | XORSTR_FORCEINLINE const_pointer crypt_get() noexcept 216 | { 217 | crypt(); 218 | return reinterpret_cast(_storage); 219 | } 220 | }; 221 | 222 | template 223 | XORSTR_FORCEINLINE constexpr auto 224 | make_xorstr(Tstr str_lambda, 225 | std::index_sequence, 226 | std::index_sequence) noexcept 227 | { 228 | return xor_string, 229 | detail::_ki()>...>{}; 230 | } 231 | 232 | } // namespace jm 233 | 234 | #endif // include guard -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "global.h" 2 | 3 | ID3D11Device *g_pdevice = nullptr; 4 | ID3D11DeviceContext *g_pcontext = nullptr; 5 | ID3D11RenderTargetView *g_prendertargetview = nullptr; 6 | WNDPROC o_wndproc = nullptr; 7 | 8 | std::once_flag present; 9 | std::once_flag wndproc; 10 | bool b_showmenu = false; 11 | bool b_box = false; 12 | bool b_chams = false; 13 | 14 | 15 | inline bool w2s(c_vec source, c_vec &destination) { 16 | return o_worldtoscreen(source, destination); 17 | } 18 | 19 | inline int get_entcount() { 20 | return *(int*)(dwbase + 0xC006788); 21 | } 22 | 23 | inline c_entity* get_player(uintptr_t idx) { 24 | return *(c_entity**)(dwbase + 0x1F96D88 + (idx << 5)); 25 | } 26 | 27 | inline c_entity* get_localentity() 28 | { 29 | uintptr_t local_entity_id = *(uintptr_t*)(dwbase + 0x1747EFC); 30 | for (int i = 0; i < get_entcount(); i++) 31 | { 32 | c_entity* ent = get_player(i); 33 | if (!ent) continue; 34 | 35 | if (!strcmp(ent->m_hHandle(), xorstr_("player"))) { 36 | if (ent->m_iId() == local_entity_id) { 37 | return ent; 38 | } 39 | } 40 | } 41 | return nullptr; 42 | } 43 | 44 | inline bool friendly(c_entity* e) { 45 | if (e->m_iTeam() == get_localentity()->m_iTeam()) 46 | return true; 47 | return false; 48 | } 49 | 50 | void render_rect(const ImVec2& from, const ImVec2& to, float rounding, uint32_t roundingCornersFlags, float thickness) 51 | { 52 | ImGuiWindow* window = ImGui::GetCurrentWindow(); 53 | window->DrawList->AddRect(from, to, ImGui::GetColorU32({ 10 / 255.0f, 60 / 255.0f, 50 / 255.0f, 1.f }), rounding, roundingCornersFlags, thickness); 54 | } 55 | 56 | LRESULT APIENTRY hk_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 57 | { 58 | std::call_once(wndproc, [&] {std::cout << xorstr_("hooked wndproc") << std::endl; }); 59 | 60 | 61 | if (uMsg == WM_KEYDOWN) { 62 | if (wParam == VK_INSERT) 63 | b_showmenu = !b_showmenu; 64 | } 65 | 66 | if (b_showmenu) { 67 | ImGui_ImplWin32_WndProcHandler(hwnd, uMsg, wParam, lParam); 68 | } 69 | 70 | return CallWindowProc(o_wndproc, hwnd, uMsg, wParam, lParam); 71 | } 72 | 73 | long __stdcall hk_present(IDXGISwapChain* p_swapchain, unsigned int syncintreval, unsigned int flags) { 74 | 75 | std::call_once(present, [&] { 76 | std::cout << xorstr_("hooked directx 11 present") << std::endl; 77 | 78 | p_swapchain->GetDevice(__uuidof(g_pdevice), reinterpret_cast(&g_pdevice)); 79 | g_pdevice->GetImmediateContext(&g_pcontext); 80 | ID3D11Texture2D *pBackBuffer; 81 | p_swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer); 82 | 83 | 84 | g_pdevice->CreateRenderTargetView(pBackBuffer, NULL, &g_prendertargetview); 85 | 86 | //shit way of doing it 87 | o_wndproc = reinterpret_cast(SetWindowLongPtrA(iat(FindWindowA).get()(0, xorstr_("Apex Legends")), GWLP_WNDPROC, reinterpret_cast(hk_wndproc))); 88 | 89 | ImGui_ImplDX11_Init(iat(FindWindowA).get()(0, xorstr_("Apex Legends")), g_pdevice, g_pcontext); 90 | ImGui_ImplDX11_CreateDeviceObjects(); 91 | }); 92 | 93 | 94 | g_pcontext->OMSetRenderTargets(1, &g_prendertargetview, NULL); 95 | 96 | 97 | ImGuiIO& io = ImGui::GetIO(); 98 | ImGui_ImplDX11_NewFrame(); 99 | ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); 100 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 0.0f, 0.0f }); 101 | ImGui::PushStyleColor(ImGuiCol_WindowBg, { 0.0f, 0.0f, 0.0f, 0.0f }); 102 | ImGui::Begin(xorstr_("##esp"), nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs); 103 | 104 | ImGui::SetWindowPos(ImVec2(0, 0), ImGuiCond_Always); 105 | ImGui::SetWindowSize(ImVec2(io.DisplaySize.x, io.DisplaySize.y), ImGuiCond_Always); 106 | for (int i = 0; i < get_entcount(); i++) { 107 | auto e = get_player(i); 108 | if (e && e->m_iHealth() > 0) { 109 | if(b_chams) 110 | e->hl_make_glow(); 111 | 112 | if (b_box) { 113 | c_vec pos, pos3D, top, top3D; 114 | pos3D = e->m_vPos(); 115 | top3D = pos3D + c_vec(0, 0, 64); 116 | 117 | if (w2s(pos3D, pos) && w2s(top3D, top)) 118 | { 119 | int height = (pos.y - top.y); 120 | int width = height / 2; 121 | render_rect(ImVec2((pos.x - width / 2), top.y), ImVec2((pos.x - width / 2) + width, top.y + height), 5, 0, 3); 122 | } 123 | 124 | } 125 | } 126 | } 127 | 128 | ImGuiWindow* window = ImGui::GetCurrentWindow(); 129 | window->DrawList->PushClipRectFullScreen(); 130 | 131 | ImGui::End(); 132 | ImGui::PopStyleColor(); 133 | ImGui::PopStyleVar(2); 134 | 135 | if (b_showmenu) { 136 | 137 | if (ImGui::Begin(xorstr_("apexlegends internal"), 0, ImVec2(200, 200))) { 138 | ImGui::Checkbox(xorstr_("glow chams"), &b_chams); 139 | ImGui::Checkbox(xorstr_("box"), &b_box); 140 | ImGui::End(); 141 | } 142 | } 143 | ImGui::Render(); 144 | return o_present(p_swapchain, syncintreval, flags); 145 | } 146 | 147 | 148 | 149 | void __stdcall _thr() { 150 | AllocConsole(); 151 | freopen(xorstr_("con"), xorstr_("w"), stdout); 152 | 153 | std::cout << xorstr_("use at ownd risk") << std::endl; 154 | 155 | while (!dwbase || !dwdiscord) { 156 | dwbase = (uintptr_t)iat(GetModuleHandleA).get()(xorstr_("r5apex.exe")); 157 | dwdiscord = (uintptr_t)iat(GetModuleHandleA).get()(xorstr_("DiscordHook64.dll")); 158 | } 159 | 160 | std::cout << xorstr_("r5apex.exe ") << std::hex << dwdiscord << std::endl; 161 | std::cout << xorstr_("discordhook64.dll ") << std::hex << dwdiscord << std::endl; 162 | 163 | std::cout << xorstr_("loading discord hook methods") << std::endl; 164 | uintptr_t dwpresent = memory::occurence(xorstr_("DiscordHook64.dll"), xorstr_("48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 8B F2 48 8B D9 48 8B D1")); 165 | o_getasynckeystate = (getasynckeystate_fn)memory::occurence(xorstr_("DiscordHook64.dll"), xorstr_("40 53 48 83 EC 20 8B D9 FF 15 ? ? ? ?")); 166 | CreateHook = (createhook_fn)(memory::occurence(xorstr_("DiscordHook64.dll"), xorstr_("40 53 55 56 57 41 54 41 56 41 57 48 83 EC 60"))); 167 | EnableHook = (enablehook_fn)(memory::occurence(xorstr_("DiscordHook64.dll"), xorstr_("48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 56 41 57 48 83 EC 20 33 F6 8B FA"))); 168 | EnableHookQue = (applyqueued_fn)(memory::occurence(xorstr_("DiscordHook64.dll"), xorstr_("48 89 5C 24 ? 48 89 6C 24 ? 48 89 7C 24 ? 41 57"))); 169 | 170 | std::cout << xorstr_("directx 11 present ") << std::hex << dwpresent << std::endl; 171 | std::cout << xorstr_("getaynckeystate ") << std::hex << o_getasynckeystate << std::endl; 172 | std::cout << xorstr_("create hook ") << std::hex << CreateHook << std::endl; 173 | std::cout << xorstr_("push hook") << std::hex << EnableHook << std::endl; 174 | std::cout << xorstr_("enable hook") << std::hex << EnableHookQue << std::endl; 175 | 176 | std::cout << xorstr_("loading game classes") << std::endl; 177 | 178 | g_pinput = (c_input*)memory::dereference(memory::occurence(xorstr_("r5apex.exe"), xorstr_("48 8D 0D ? ? ? ? 41 FF 90 ? ? ? ? EB E9")), 3); 179 | g_pglobals = (c_globalvars*)memory::dereference(memory::occurence(xorstr_("r5apex.exe"), xorstr_("48 8B 05 ? ? ? ? F3 0F 10 50 ? 74 38")), 4); 180 | createinterface = (createinterface_fn)memory::dereference(memory::occurence(xorstr_("r5apex.exe"), xorstr_("E8 ? ? ? ? 48 89 05 ? ? ? ? 48 83 3D ? ? ? ? ? 0F 84 ? ? ? ? 33 D2")), 1); 181 | o_worldtoscreen = (worldtoscreen_fn)(dwbase + 0x642B10); 182 | 183 | std::cout << xorstr_("g_pinput ") << std::hex << g_pinput << std::endl; 184 | std::cout << xorstr_("g_pglobals ") << std::hex << g_pglobals << std::endl; 185 | std::cout << xorstr_("createinterface ") << std::hex << createinterface << std::endl; 186 | std::cout << xorstr_("o_worldtoscreen ") << std::hex << o_worldtoscreen << std::endl; 187 | 188 | std::cout << xorstr_("enable hooks") << std::endl; 189 | 190 | CreateHook((void*)dwpresent, (void*)hk_present, (void**)&o_present); 191 | EnableHook((void*)dwpresent, 1); 192 | EnableHookQue(); 193 | } 194 | 195 | bool __stdcall DllMain(void* module, unsigned long reason, void* buffer) {//inject with hack thread option 196 | if (reason == 1) { 197 | _thr(); 198 | } 199 | return true; 200 | } 201 | 202 | -------------------------------------------------------------------------------- /apexinternal.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {E5BA3482-8AD3-4589-B8BE-DC9AFFBFE17E} 24 | apexinternal 25 | 10.0.17763.0 26 | apexinternal 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | MultiByte 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | MultiByte 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v141 46 | MultiByte 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v141 52 | true 53 | MultiByte 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | $(VC_IncludePath);$(DXSDK_DIR)Include;$(WindowsSDK_IncludePath); 75 | $(VC_LibraryPath_x64);$(DXSDK_DIR)Lib\x64;$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64 76 | 77 | 78 | $(VC_IncludePath);$(DXSDK_DIR)Include;$(WindowsSDK_IncludePath); 79 | $(VC_LibraryPath_x64);$(DXSDK_DIR)Lib\x64;$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64 80 | 81 | 82 | 83 | Level3 84 | Disabled 85 | true 86 | true 87 | 88 | 89 | 90 | 91 | Level3 92 | Disabled 93 | true 94 | true 95 | stdcpp17 96 | 97 | 98 | DebugFull 99 | 100 | 101 | 102 | 103 | Level3 104 | MaxSpeed 105 | true 106 | true 107 | true 108 | true 109 | 110 | 111 | true 112 | true 113 | 114 | 115 | 116 | 117 | TurnOffAllWarnings 118 | MaxSpeed 119 | true 120 | true 121 | false 122 | true 123 | AnySuitable 124 | stdcpp17 125 | 126 | 127 | true 128 | true 129 | false 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /gui/stb_rect_pack.h: -------------------------------------------------------------------------------- 1 | // stb_rect_pack.h - v0.10 - public domain - rectangle packing 2 | // Sean Barrett 2014 3 | // 4 | // Useful for e.g. packing rectangular textures into an atlas. 5 | // Does not do rotation. 6 | // 7 | // Not necessarily the awesomest packing method, but better than 8 | // the totally naive one in stb_truetype (which is primarily what 9 | // this is meant to replace). 10 | // 11 | // Has only had a few tests run, may have issues. 12 | // 13 | // More docs to come. 14 | // 15 | // No memory allocations; uses qsort() and assert() from stdlib. 16 | // Can override those by defining STBRP_SORT and STBRP_ASSERT. 17 | // 18 | // This library currently uses the Skyline Bottom-Left algorithm. 19 | // 20 | // Please note: better rectangle packers are welcome! Please 21 | // implement them to the same API, but with a different init 22 | // function. 23 | // 24 | // Credits 25 | // 26 | // Library 27 | // Sean Barrett 28 | // Minor features 29 | // Martins Mozeiko 30 | // Bugfixes / warning fixes 31 | // Jeremy Jaussaud 32 | // 33 | // Version history: 34 | // 35 | // 0.10 (2016-10-25) remove cast-away-const to avoid warnings 36 | // 0.09 (2016-08-27) fix compiler warnings 37 | // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) 38 | // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) 39 | // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort 40 | // 0.05: added STBRP_ASSERT to allow replacing assert 41 | // 0.04: fixed minor bug in STBRP_LARGE_RECTS support 42 | // 0.01: initial release 43 | // 44 | // LICENSE 45 | // 46 | // This software is dual-licensed to the public domain and under the following 47 | // license: you are granted a perpetual, irrevocable license to copy, modify, 48 | // publish, and distribute this file as you see fit. 49 | 50 | ////////////////////////////////////////////////////////////////////////////// 51 | // 52 | // INCLUDE SECTION 53 | // 54 | 55 | #ifndef STB_INCLUDE_STB_RECT_PACK_H 56 | #define STB_INCLUDE_STB_RECT_PACK_H 57 | 58 | #define STB_RECT_PACK_VERSION 1 59 | 60 | #ifdef STBRP_STATIC 61 | #define STBRP_DEF static 62 | #else 63 | #define STBRP_DEF extern 64 | #endif 65 | 66 | #ifdef __cplusplus 67 | extern "C" { 68 | #endif 69 | 70 | typedef struct stbrp_context stbrp_context; 71 | typedef struct stbrp_node stbrp_node; 72 | typedef struct stbrp_rect stbrp_rect; 73 | 74 | #ifdef STBRP_LARGE_RECTS 75 | typedef int stbrp_coord; 76 | #else 77 | typedef unsigned short stbrp_coord; 78 | #endif 79 | 80 | STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects); 81 | // Assign packed locations to rectangles. The rectangles are of type 82 | // 'stbrp_rect' defined below, stored in the array 'rects', and there 83 | // are 'num_rects' many of them. 84 | // 85 | // Rectangles which are successfully packed have the 'was_packed' flag 86 | // set to a non-zero value and 'x' and 'y' store the minimum location 87 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left 88 | // if you imagine y increasing downwards). Rectangles which do not fit 89 | // have the 'was_packed' flag set to 0. 90 | // 91 | // You should not try to access the 'rects' array from another thread 92 | // while this function is running, as the function temporarily reorders 93 | // the array while it executes. 94 | // 95 | // To pack into another rectangle, you need to call stbrp_init_target 96 | // again. To continue packing into the same rectangle, you can call 97 | // this function again. Calling this multiple times with multiple rect 98 | // arrays will probably produce worse packing results than calling it 99 | // a single time with the full rectangle array, but the option is 100 | // available. 101 | 102 | struct stbrp_rect 103 | { 104 | // reserved for your use: 105 | int id; 106 | 107 | // input: 108 | stbrp_coord w, h; 109 | 110 | // output: 111 | stbrp_coord x, y; 112 | int was_packed; // non-zero if valid packing 113 | 114 | }; // 16 bytes, nominally 115 | 116 | 117 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); 118 | // Initialize a rectangle packer to: 119 | // pack a rectangle that is 'width' by 'height' in dimensions 120 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long 121 | // 122 | // You must call this function every time you start packing into a new target. 123 | // 124 | // There is no "shutdown" function. The 'nodes' memory must stay valid for 125 | // the following stbrp_pack_rects() call (or calls), but can be freed after 126 | // the call (or calls) finish. 127 | // 128 | // Note: to guarantee best results, either: 129 | // 1. make sure 'num_nodes' >= 'width' 130 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 131 | // 132 | // If you don't do either of the above things, widths will be quantized to multiples 133 | // of small integers to guarantee the algorithm doesn't run out of temporary storage. 134 | // 135 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm 136 | // may run out of temporary storage and be unable to pack some rectangles. 137 | 138 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem); 139 | // Optionally call this function after init but before doing any packing to 140 | // change the handling of the out-of-temp-memory scenario, described above. 141 | // If you call init again, this will be reset to the default (false). 142 | 143 | 144 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic); 145 | // Optionally select which packing heuristic the library should use. Different 146 | // heuristics will produce better/worse results for different data sets. 147 | // If you call init again, this will be reset to the default. 148 | 149 | enum 150 | { 151 | STBRP_HEURISTIC_Skyline_default = 0, 152 | STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, 153 | STBRP_HEURISTIC_Skyline_BF_sortHeight 154 | }; 155 | 156 | 157 | ////////////////////////////////////////////////////////////////////////////// 158 | // 159 | // the details of the following structures don't matter to you, but they must 160 | // be visible so you can handle the memory allocations for them 161 | 162 | struct stbrp_node 163 | { 164 | stbrp_coord x, y; 165 | stbrp_node *next; 166 | }; 167 | 168 | struct stbrp_context 169 | { 170 | int width; 171 | int height; 172 | int align; 173 | int init_mode; 174 | int heuristic; 175 | int num_nodes; 176 | stbrp_node *active_head; 177 | stbrp_node *free_head; 178 | stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 179 | }; 180 | 181 | #ifdef __cplusplus 182 | } 183 | #endif 184 | 185 | #endif 186 | 187 | ////////////////////////////////////////////////////////////////////////////// 188 | // 189 | // IMPLEMENTATION SECTION 190 | // 191 | 192 | #ifdef STB_RECT_PACK_IMPLEMENTATION 193 | #ifndef STBRP_SORT 194 | #include 195 | #define STBRP_SORT qsort 196 | #endif 197 | 198 | #ifndef STBRP_ASSERT 199 | #include 200 | #define STBRP_ASSERT assert 201 | #endif 202 | 203 | #ifdef _MSC_VER 204 | #define STBRP__NOTUSED(v) (void)(v) 205 | #else 206 | #define STBRP__NOTUSED(v) (void)sizeof(v) 207 | #endif 208 | 209 | enum 210 | { 211 | STBRP__INIT_skyline = 1 212 | }; 213 | 214 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) 215 | { 216 | switch (context->init_mode) { 217 | case STBRP__INIT_skyline: 218 | STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); 219 | context->heuristic = heuristic; 220 | break; 221 | default: 222 | STBRP_ASSERT(0); 223 | } 224 | } 225 | 226 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) 227 | { 228 | if (allow_out_of_mem) 229 | // if it's ok to run out of memory, then don't bother aligning them; 230 | // this gives better packing, but may fail due to OOM (even though 231 | // the rectangles easily fit). @TODO a smarter approach would be to only 232 | // quantize once we've hit OOM, then we could get rid of this parameter. 233 | context->align = 1; 234 | else { 235 | // if it's not ok to run out of memory, then quantize the widths 236 | // so that num_nodes is always enough nodes. 237 | // 238 | // I.e. num_nodes * align >= width 239 | // align >= width / num_nodes 240 | // align = ceil(width/num_nodes) 241 | 242 | context->align = (context->width + context->num_nodes - 1) / context->num_nodes; 243 | } 244 | } 245 | 246 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) 247 | { 248 | int i; 249 | #ifndef STBRP_LARGE_RECTS 250 | STBRP_ASSERT(width <= 0xffff && height <= 0xffff); 251 | #endif 252 | 253 | for (i = 0; i < num_nodes - 1; ++i) 254 | nodes[i].next = &nodes[i + 1]; 255 | nodes[i].next = NULL; 256 | context->init_mode = STBRP__INIT_skyline; 257 | context->heuristic = STBRP_HEURISTIC_Skyline_default; 258 | context->free_head = &nodes[0]; 259 | context->active_head = &context->extra[0]; 260 | context->width = width; 261 | context->height = height; 262 | context->num_nodes = num_nodes; 263 | stbrp_setup_allow_out_of_mem(context, 0); 264 | 265 | // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) 266 | context->extra[0].x = 0; 267 | context->extra[0].y = 0; 268 | context->extra[0].next = &context->extra[1]; 269 | context->extra[1].x = (stbrp_coord)width; 270 | #ifdef STBRP_LARGE_RECTS 271 | context->extra[1].y = (1 << 30); 272 | #else 273 | context->extra[1].y = 65535; 274 | #endif 275 | context->extra[1].next = NULL; 276 | } 277 | 278 | // find minimum y position if it starts at x1 279 | static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) 280 | { 281 | stbrp_node *node = first; 282 | int x1 = x0 + width; 283 | int min_y, visited_width, waste_area; 284 | 285 | STBRP__NOTUSED(c); 286 | 287 | STBRP_ASSERT(first->x <= x0); 288 | 289 | #if 0 290 | // skip in case we're past the node 291 | while (node->next->x <= x0) 292 | ++node; 293 | #else 294 | STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency 295 | #endif 296 | 297 | STBRP_ASSERT(node->x <= x0); 298 | 299 | min_y = 0; 300 | waste_area = 0; 301 | visited_width = 0; 302 | while (node->x < x1) { 303 | if (node->y > min_y) { 304 | // raise min_y higher. 305 | // we've accounted for all waste up to min_y, 306 | // but we'll now add more waste for everything we've visted 307 | waste_area += visited_width * (node->y - min_y); 308 | min_y = node->y; 309 | // the first time through, visited_width might be reduced 310 | if (node->x < x0) 311 | visited_width += node->next->x - x0; 312 | else 313 | visited_width += node->next->x - node->x; 314 | } 315 | else { 316 | // add waste area 317 | int under_width = node->next->x - node->x; 318 | if (under_width + visited_width > width) 319 | under_width = width - visited_width; 320 | waste_area += under_width * (min_y - node->y); 321 | visited_width += under_width; 322 | } 323 | node = node->next; 324 | } 325 | 326 | *pwaste = waste_area; 327 | return min_y; 328 | } 329 | 330 | typedef struct 331 | { 332 | int x, y; 333 | stbrp_node **prev_link; 334 | } stbrp__findresult; 335 | 336 | static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) 337 | { 338 | int best_waste = (1 << 30), best_x, best_y = (1 << 30); 339 | stbrp__findresult fr; 340 | stbrp_node **prev, *node, *tail, **best = NULL; 341 | 342 | // align to multiple of c->align 343 | width = (width + c->align - 1); 344 | width -= width % c->align; 345 | STBRP_ASSERT(width % c->align == 0); 346 | 347 | node = c->active_head; 348 | prev = &c->active_head; 349 | while (node->x + width <= c->width) { 350 | int y, waste; 351 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 352 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL 353 | // bottom left 354 | if (y < best_y) { 355 | best_y = y; 356 | best = prev; 357 | } 358 | } 359 | else { 360 | // best-fit 361 | if (y + height <= c->height) { 362 | // can only use it if it first vertically 363 | if (y < best_y || (y == best_y && waste < best_waste)) { 364 | best_y = y; 365 | best_waste = waste; 366 | best = prev; 367 | } 368 | } 369 | } 370 | prev = &node->next; 371 | node = node->next; 372 | } 373 | 374 | best_x = (best == NULL) ? 0 : (*best)->x; 375 | 376 | // if doing best-fit (BF), we also have to try aligning right edge to each node position 377 | // 378 | // e.g, if fitting 379 | // 380 | // ____________________ 381 | // |____________________| 382 | // 383 | // into 384 | // 385 | // | | 386 | // | ____________| 387 | // |____________| 388 | // 389 | // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned 390 | // 391 | // This makes BF take about 2x the time 392 | 393 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { 394 | tail = c->active_head; 395 | node = c->active_head; 396 | prev = &c->active_head; 397 | // find first node that's admissible 398 | while (tail->x < width) 399 | tail = tail->next; 400 | while (tail) { 401 | int xpos = tail->x - width; 402 | int y, waste; 403 | STBRP_ASSERT(xpos >= 0); 404 | // find the left position that matches this 405 | while (node->next->x <= xpos) { 406 | prev = &node->next; 407 | node = node->next; 408 | } 409 | STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); 410 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 411 | if (y + height < c->height) { 412 | if (y <= best_y) { 413 | if (y < best_y || waste < best_waste || (waste == best_waste && xpos < best_x)) { 414 | best_x = xpos; 415 | STBRP_ASSERT(y <= best_y); 416 | best_y = y; 417 | best_waste = waste; 418 | best = prev; 419 | } 420 | } 421 | } 422 | tail = tail->next; 423 | } 424 | } 425 | 426 | fr.prev_link = best; 427 | fr.x = best_x; 428 | fr.y = best_y; 429 | return fr; 430 | } 431 | 432 | static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) 433 | { 434 | // find best position according to heuristic 435 | stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); 436 | stbrp_node *node, *cur; 437 | 438 | // bail if: 439 | // 1. it failed 440 | // 2. the best node doesn't fit (we don't always check this) 441 | // 3. we're out of memory 442 | if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { 443 | res.prev_link = NULL; 444 | return res; 445 | } 446 | 447 | // on success, create new node 448 | node = context->free_head; 449 | node->x = (stbrp_coord)res.x; 450 | node->y = (stbrp_coord)(res.y + height); 451 | 452 | context->free_head = node->next; 453 | 454 | // insert the new node into the right starting point, and 455 | // let 'cur' point to the remaining nodes needing to be 456 | // stiched back in 457 | 458 | cur = *res.prev_link; 459 | if (cur->x < res.x) { 460 | // preserve the existing one, so start testing with the next one 461 | stbrp_node *next = cur->next; 462 | cur->next = node; 463 | cur = next; 464 | } 465 | else { 466 | *res.prev_link = node; 467 | } 468 | 469 | // from here, traverse cur and free the nodes, until we get to one 470 | // that shouldn't be freed 471 | while (cur->next && cur->next->x <= res.x + width) { 472 | stbrp_node *next = cur->next; 473 | // move the current node to the free list 474 | cur->next = context->free_head; 475 | context->free_head = cur; 476 | cur = next; 477 | } 478 | 479 | // stitch the list back in 480 | node->next = cur; 481 | 482 | if (cur->x < res.x + width) 483 | cur->x = (stbrp_coord)(res.x + width); 484 | 485 | #ifdef _DEBUG 486 | cur = context->active_head; 487 | while (cur->x < context->width) { 488 | STBRP_ASSERT(cur->x < cur->next->x); 489 | cur = cur->next; 490 | } 491 | STBRP_ASSERT(cur->next == NULL); 492 | 493 | { 494 | stbrp_node *L1 = NULL, *L2 = NULL; 495 | int count = 0; 496 | cur = context->active_head; 497 | while (cur) { 498 | L1 = cur; 499 | cur = cur->next; 500 | ++count; 501 | } 502 | cur = context->free_head; 503 | while (cur) { 504 | L2 = cur; 505 | cur = cur->next; 506 | ++count; 507 | } 508 | STBRP_ASSERT(count == context->num_nodes + 2); 509 | } 510 | #endif 511 | 512 | return res; 513 | } 514 | 515 | static int rect_height_compare(const void *a, const void *b) 516 | { 517 | const stbrp_rect *p = (const stbrp_rect *)a; 518 | const stbrp_rect *q = (const stbrp_rect *)b; 519 | if (p->h > q->h) 520 | return -1; 521 | if (p->h < q->h) 522 | return 1; 523 | return (p->w > q->w) ? -1 : (p->w < q->w); 524 | } 525 | 526 | static int rect_width_compare(const void *a, const void *b) 527 | { 528 | const stbrp_rect *p = (const stbrp_rect *)a; 529 | const stbrp_rect *q = (const stbrp_rect *)b; 530 | if (p->w > q->w) 531 | return -1; 532 | if (p->w < q->w) 533 | return 1; 534 | return (p->h > q->h) ? -1 : (p->h < q->h); 535 | } 536 | 537 | static int rect_original_order(const void *a, const void *b) 538 | { 539 | const stbrp_rect *p = (const stbrp_rect *)a; 540 | const stbrp_rect *q = (const stbrp_rect *)b; 541 | return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); 542 | } 543 | 544 | #ifdef STBRP_LARGE_RECTS 545 | #define STBRP__MAXVAL 0xffffffff 546 | #else 547 | #define STBRP__MAXVAL 0xffff 548 | #endif 549 | 550 | STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) 551 | { 552 | int i; 553 | 554 | // we use the 'was_packed' field internally to allow sorting/unsorting 555 | for (i = 0; i < num_rects; ++i) { 556 | rects[i].was_packed = i; 557 | #ifndef STBRP_LARGE_RECTS 558 | STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); 559 | #endif 560 | } 561 | 562 | // sort according to heuristic 563 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); 564 | 565 | for (i = 0; i < num_rects; ++i) { 566 | if (rects[i].w == 0 || rects[i].h == 0) { 567 | rects[i].x = rects[i].y = 0; // empty rect needs no space 568 | } 569 | else { 570 | stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 571 | if (fr.prev_link) { 572 | rects[i].x = (stbrp_coord)fr.x; 573 | rects[i].y = (stbrp_coord)fr.y; 574 | } 575 | else { 576 | rects[i].x = rects[i].y = STBRP__MAXVAL; 577 | } 578 | } 579 | } 580 | 581 | // unsort 582 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); 583 | 584 | // set was_packed flags 585 | for (i = 0; i < num_rects; ++i) 586 | rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); 587 | } 588 | #endif 589 | -------------------------------------------------------------------------------- /lazyimporter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef LAZY_IMPORTER_HPP 3 | #define LAZY_IMPORTER_HPP 4 | 5 | #define iat(name) \ 6 | ::li::detail::lazy_function<::li::detail::khash(#name), decltype(&name)>() 7 | 8 | #define LI_FN_DEF(name) ::li::detail::lazy_function<::li::detail::khash(#name), name>() 9 | 10 | #define LI_MODULE(name) ::li::detail::lazy_module<::li::detail::khash(name)>() 11 | 12 | // NOTE only std::forward is used from this header. 13 | // If there is a need to eliminate this dependency the function itself is very small. 14 | #include 15 | #include 16 | #include 17 | 18 | #ifndef LAZY_IMPORTER_NO_FORCEINLINE 19 | #if defined(_MSC_VER) 20 | #define LAZY_IMPORTER_FORCEINLINE __forceinline 21 | #elif defined(__GNUC__) && __GNUC__ > 3 22 | #define LAZY_IMPORTER_FORCEINLINE inline __attribute__((__always_inline__)) 23 | #else 24 | #define LAZY_IMPORTER_FORCEINLINE inline 25 | #endif 26 | #else 27 | #define LAZY_IMPORTER_FORCEINLINE inline 28 | #endif 29 | 30 | #ifdef LAZY_IMPORTER_CASE_INSENSITIVE 31 | #define LAZY_IMPORTER_TOLOWER(c) (c >= 'A' && c <= 'Z' ? (c | (1 << 5)) : c) 32 | #else 33 | #define LAZY_IMPORTER_TOLOWER(c) (c) 34 | #endif 35 | 36 | namespace li { 37 | namespace detail { 38 | 39 | template 40 | struct pair { 41 | First first; 42 | Second second; 43 | }; 44 | 45 | namespace win { 46 | 47 | struct LIST_ENTRY_T { 48 | const char* Flink; 49 | const char* Blink; 50 | }; 51 | 52 | struct UNICODE_STRING_T { 53 | unsigned short Length; 54 | unsigned short MaximumLength; 55 | wchar_t* Buffer; 56 | }; 57 | 58 | struct PEB_LDR_DATA_T { 59 | unsigned long Length; 60 | unsigned long Initialized; 61 | const char* SsHandle; 62 | LIST_ENTRY_T InLoadOrderModuleList; 63 | }; 64 | 65 | struct PEB_T { 66 | unsigned char Reserved1[2]; 67 | unsigned char BeingDebugged; 68 | unsigned char Reserved2[1]; 69 | const char* Reserved3[2]; 70 | PEB_LDR_DATA_T* Ldr; 71 | }; 72 | 73 | struct LDR_DATA_TABLE_ENTRY_T { 74 | LIST_ENTRY_T InLoadOrderLinks; 75 | LIST_ENTRY_T InMemoryOrderLinks; 76 | LIST_ENTRY_T InInitializationOrderLinks; 77 | const char* DllBase; 78 | const char* EntryPoint; 79 | union { 80 | unsigned long SizeOfImage; 81 | const char* _dummy; 82 | }; 83 | UNICODE_STRING_T FullDllName; 84 | UNICODE_STRING_T BaseDllName; 85 | 86 | LAZY_IMPORTER_FORCEINLINE const LDR_DATA_TABLE_ENTRY_T* 87 | load_order_next() const noexcept 88 | { 89 | return reinterpret_cast( 90 | InLoadOrderLinks.Flink); 91 | } 92 | }; 93 | 94 | struct IMAGE_DOS_HEADER { // DOS .EXE header 95 | unsigned short e_magic; // Magic number 96 | unsigned short e_cblp; // Bytes on last page of file 97 | unsigned short e_cp; // Pages in file 98 | unsigned short e_crlc; // Relocations 99 | unsigned short e_cparhdr; // Size of header in paragraphs 100 | unsigned short e_minalloc; // Minimum extra paragraphs needed 101 | unsigned short e_maxalloc; // Maximum extra paragraphs needed 102 | unsigned short e_ss; // Initial (relative) SS value 103 | unsigned short e_sp; // Initial SP value 104 | unsigned short e_csum; // Checksum 105 | unsigned short e_ip; // Initial IP value 106 | unsigned short e_cs; // Initial (relative) CS value 107 | unsigned short e_lfarlc; // File address of relocation table 108 | unsigned short e_ovno; // Overlay number 109 | unsigned short e_res[4]; // Reserved words 110 | unsigned short e_oemid; // OEM identifier (for e_oeminfo) 111 | unsigned short e_oeminfo; // OEM information; e_oemid specific 112 | unsigned short e_res2[10]; // Reserved words 113 | long e_lfanew; // File address of new exe header 114 | }; 115 | 116 | struct IMAGE_FILE_HEADER { 117 | unsigned short Machine; 118 | unsigned short NumberOfSections; 119 | unsigned long TimeDateStamp; 120 | unsigned long PointerToSymbolTable; 121 | unsigned long NumberOfSymbols; 122 | unsigned short SizeOfOptionalHeader; 123 | unsigned short Characteristics; 124 | }; 125 | 126 | struct IMAGE_EXPORT_DIRECTORY { 127 | unsigned long Characteristics; 128 | unsigned long TimeDateStamp; 129 | unsigned short MajorVersion; 130 | unsigned short MinorVersion; 131 | unsigned long Name; 132 | unsigned long Base; 133 | unsigned long NumberOfFunctions; 134 | unsigned long NumberOfNames; 135 | unsigned long AddressOfFunctions; // RVA from base of image 136 | unsigned long AddressOfNames; // RVA from base of image 137 | unsigned long AddressOfNameOrdinals; // RVA from base of image 138 | }; 139 | 140 | struct IMAGE_DATA_DIRECTORY { 141 | unsigned long VirtualAddress; 142 | unsigned long Size; 143 | }; 144 | 145 | struct IMAGE_OPTIONAL_HEADER64 { 146 | unsigned short Magic; 147 | unsigned char MajorLinkerVersion; 148 | unsigned char MinorLinkerVersion; 149 | unsigned long SizeOfCode; 150 | unsigned long SizeOfInitializedData; 151 | unsigned long SizeOfUninitializedData; 152 | unsigned long AddressOfEntryPoint; 153 | unsigned long BaseOfCode; 154 | unsigned long long ImageBase; 155 | unsigned long SectionAlignment; 156 | unsigned long FileAlignment; 157 | unsigned short MajorOperatingSystemVersion; 158 | unsigned short MinorOperatingSystemVersion; 159 | unsigned short MajorImageVersion; 160 | unsigned short MinorImageVersion; 161 | unsigned short MajorSubsystemVersion; 162 | unsigned short MinorSubsystemVersion; 163 | unsigned long Win32VersionValue; 164 | unsigned long SizeOfImage; 165 | unsigned long SizeOfHeaders; 166 | unsigned long CheckSum; 167 | unsigned short Subsystem; 168 | unsigned short DllCharacteristics; 169 | unsigned long long SizeOfStackReserve; 170 | unsigned long long SizeOfStackCommit; 171 | unsigned long long SizeOfHeapReserve; 172 | unsigned long long SizeOfHeapCommit; 173 | unsigned long LoaderFlags; 174 | unsigned long NumberOfRvaAndSizes; 175 | IMAGE_DATA_DIRECTORY DataDirectory[16]; 176 | }; 177 | 178 | struct IMAGE_OPTIONAL_HEADER32 { 179 | unsigned short Magic; 180 | unsigned char MajorLinkerVersion; 181 | unsigned char MinorLinkerVersion; 182 | unsigned long SizeOfCode; 183 | unsigned long SizeOfInitializedData; 184 | unsigned long SizeOfUninitializedData; 185 | unsigned long AddressOfEntryPoint; 186 | unsigned long BaseOfCode; 187 | unsigned long BaseOfData; 188 | unsigned long ImageBase; 189 | unsigned long SectionAlignment; 190 | unsigned long FileAlignment; 191 | unsigned short MajorOperatingSystemVersion; 192 | unsigned short MinorOperatingSystemVersion; 193 | unsigned short MajorImageVersion; 194 | unsigned short MinorImageVersion; 195 | unsigned short MajorSubsystemVersion; 196 | unsigned short MinorSubsystemVersion; 197 | unsigned long Win32VersionValue; 198 | unsigned long SizeOfImage; 199 | unsigned long SizeOfHeaders; 200 | unsigned long CheckSum; 201 | unsigned short Subsystem; 202 | unsigned short DllCharacteristics; 203 | unsigned long SizeOfStackReserve; 204 | unsigned long SizeOfStackCommit; 205 | unsigned long SizeOfHeapReserve; 206 | unsigned long SizeOfHeapCommit; 207 | unsigned long LoaderFlags; 208 | unsigned long NumberOfRvaAndSizes; 209 | IMAGE_DATA_DIRECTORY DataDirectory[16]; 210 | }; 211 | 212 | struct IMAGE_NT_HEADERS { 213 | unsigned long Signature; 214 | IMAGE_FILE_HEADER FileHeader; 215 | #ifdef _WIN64 216 | IMAGE_OPTIONAL_HEADER64 OptionalHeader; 217 | #else 218 | IMAGE_OPTIONAL_HEADER32 OptionalHeader; 219 | #endif 220 | }; 221 | 222 | } // namespace win 223 | 224 | // hashing stuff 225 | struct hash_t { 226 | using value_type = unsigned long; 227 | constexpr static value_type offset = 2166136261; 228 | constexpr static value_type prime = 16777619; 229 | constexpr static unsigned long long prime64 = prime; 230 | 231 | LAZY_IMPORTER_FORCEINLINE constexpr static value_type single(value_type value, 232 | char c) noexcept 233 | { 234 | return static_cast( 235 | (value ^ LAZY_IMPORTER_TOLOWER(c)) * 236 | static_cast(prime)); 237 | } 238 | }; 239 | 240 | template 241 | LAZY_IMPORTER_FORCEINLINE constexpr hash_t::value_type 242 | khash(const CharT* str, hash_t::value_type value = hash_t::offset) noexcept 243 | { 244 | return (*str ? khash(str + 1, hash_t::single(value, *str)) : value); 245 | } 246 | 247 | template 248 | LAZY_IMPORTER_FORCEINLINE hash_t::value_type hash(const CharT* str) noexcept 249 | { 250 | hash_t::value_type value = hash_t::offset; 251 | 252 | for (;;) { 253 | char c = *str++; 254 | if (!c) 255 | return value; 256 | value = hash_t::single(value, c); 257 | } 258 | } 259 | 260 | LAZY_IMPORTER_FORCEINLINE hash_t::value_type hash( 261 | const win::UNICODE_STRING_T& str) noexcept 262 | { 263 | auto first = str.Buffer; 264 | const auto last = first + (str.Length / sizeof(wchar_t)); 265 | auto value = hash_t::offset; 266 | for (; first != last; ++first) 267 | value = hash_t::single(value, static_cast(*first)); 268 | 269 | return value; 270 | } 271 | 272 | LAZY_IMPORTER_FORCEINLINE pair hash_forwarded( 273 | const char* str) noexcept 274 | { 275 | pair module_and_function{ 276 | hash_t::offset, hash_t::offset 277 | }; 278 | 279 | for (; *str != '.'; ++str) 280 | hash_t::single(module_and_function.first, *str); 281 | 282 | ++str; 283 | 284 | for (; *str; ++str) 285 | hash_t::single(module_and_function.second, *str); 286 | 287 | return module_and_function; 288 | } 289 | 290 | 291 | // some helper functions 292 | LAZY_IMPORTER_FORCEINLINE const win::PEB_T* peb() noexcept 293 | { 294 | #if defined(_WIN64) 295 | return reinterpret_cast(__readgsqword(0x60)); 296 | #elif defined(_WIN32) 297 | return reinterpret_cast(__readfsdword(0x30)); 298 | #else 299 | #error Unsupported platform. Open an issue and I'll probably add support. 300 | #endif 301 | } 302 | 303 | LAZY_IMPORTER_FORCEINLINE const win::PEB_LDR_DATA_T* ldr() 304 | { 305 | return reinterpret_cast(peb()->Ldr); 306 | } 307 | 308 | LAZY_IMPORTER_FORCEINLINE const win::IMAGE_NT_HEADERS* nt_headers( 309 | const char* base) noexcept 310 | { 311 | return reinterpret_cast( 312 | base + reinterpret_cast(base)->e_lfanew); 313 | } 314 | 315 | LAZY_IMPORTER_FORCEINLINE const win::IMAGE_EXPORT_DIRECTORY* image_export_dir( 316 | const char* base) noexcept 317 | { 318 | return reinterpret_cast( 319 | base + nt_headers(base)->OptionalHeader.DataDirectory->VirtualAddress); 320 | } 321 | 322 | LAZY_IMPORTER_FORCEINLINE const win::LDR_DATA_TABLE_ENTRY_T* ldr_data_entry() noexcept 323 | { 324 | return reinterpret_cast( 325 | ldr()->InLoadOrderModuleList.Flink); 326 | } 327 | 328 | struct exports_directory { 329 | const char* _base; 330 | const win::IMAGE_EXPORT_DIRECTORY* _ied; 331 | unsigned long _ied_size; 332 | 333 | public: 334 | using size_type = unsigned long; 335 | 336 | LAZY_IMPORTER_FORCEINLINE 337 | exports_directory(const char* base) noexcept : _base(base) 338 | { 339 | const auto ied_data_dir = nt_headers(base)->OptionalHeader.DataDirectory[0]; 340 | _ied = reinterpret_cast( 341 | base + ied_data_dir.VirtualAddress); 342 | _ied_size = ied_data_dir.Size; 343 | } 344 | 345 | LAZY_IMPORTER_FORCEINLINE explicit operator bool() const noexcept 346 | { 347 | return reinterpret_cast(_ied) != _base; 348 | } 349 | 350 | LAZY_IMPORTER_FORCEINLINE size_type size() const noexcept 351 | { 352 | return _ied->NumberOfNames; 353 | } 354 | 355 | LAZY_IMPORTER_FORCEINLINE const char* base() const noexcept { return _base; } 356 | LAZY_IMPORTER_FORCEINLINE const win::IMAGE_EXPORT_DIRECTORY* ied() const noexcept 357 | { 358 | return _ied; 359 | } 360 | 361 | LAZY_IMPORTER_FORCEINLINE const char* name(size_type index) const noexcept 362 | { 363 | return reinterpret_cast( 364 | _base + reinterpret_cast( 365 | _base + _ied->AddressOfNames)[index]); 366 | } 367 | 368 | LAZY_IMPORTER_FORCEINLINE const char* address(size_type index) const noexcept 369 | { 370 | const auto* const rva_table = 371 | reinterpret_cast(_base + _ied->AddressOfFunctions); 372 | 373 | const auto* const ord_table = reinterpret_cast( 374 | _base + _ied->AddressOfNameOrdinals); 375 | 376 | return _base + rva_table[ord_table[index]]; 377 | } 378 | 379 | LAZY_IMPORTER_FORCEINLINE bool is_forwarded(const char* export_address) const 380 | noexcept 381 | { 382 | const auto ui_ied = reinterpret_cast(_ied); 383 | return (export_address > ui_ied && export_address < ui_ied + _ied_size); 384 | } 385 | }; 386 | 387 | struct safe_module_enumerator { 388 | using value_type = const detail::win::LDR_DATA_TABLE_ENTRY_T; 389 | value_type* value; 390 | value_type* const head; 391 | 392 | LAZY_IMPORTER_FORCEINLINE safe_module_enumerator() noexcept 393 | : value(ldr_data_entry()), head(value) 394 | {} 395 | 396 | LAZY_IMPORTER_FORCEINLINE void reset() noexcept { value = head; } 397 | 398 | LAZY_IMPORTER_FORCEINLINE bool next() noexcept 399 | { 400 | value = value->load_order_next(); 401 | return value != head; 402 | } 403 | }; 404 | 405 | struct unsafe_module_enumerator { 406 | using value_type = const detail::win::LDR_DATA_TABLE_ENTRY_T*; 407 | value_type value; 408 | 409 | LAZY_IMPORTER_FORCEINLINE unsafe_module_enumerator() noexcept 410 | : value(ldr_data_entry()) 411 | {} 412 | 413 | LAZY_IMPORTER_FORCEINLINE void reset() noexcept { value = ldr_data_entry(); } 414 | 415 | LAZY_IMPORTER_FORCEINLINE bool next() noexcept 416 | { 417 | value = value->load_order_next(); 418 | return true; 419 | } 420 | }; 421 | 422 | // provides the cached functions which use Derive classes methods 423 | template 424 | class lazy_base { 425 | protected: 426 | // This function is needed because every templated function 427 | // with different args has its own static buffer 428 | LAZY_IMPORTER_FORCEINLINE static void*& _cache() noexcept 429 | { 430 | static void* value = nullptr; 431 | return value; 432 | } 433 | 434 | public: 435 | template 436 | LAZY_IMPORTER_FORCEINLINE static T safe() noexcept 437 | { 438 | return Derived::template get(); 439 | } 440 | 441 | template 442 | LAZY_IMPORTER_FORCEINLINE static T cached() noexcept 443 | { 444 | auto& cached = _cache(); 445 | if (!cached) 446 | cached = Derived::template get(); 447 | 448 | return (T)(cached); 449 | } 450 | 451 | template 452 | LAZY_IMPORTER_FORCEINLINE static T safe_cached() noexcept 453 | { 454 | return cached(); 455 | } 456 | }; 457 | 458 | template 459 | struct lazy_module : lazy_base> { 460 | template 461 | LAZY_IMPORTER_FORCEINLINE static T get() noexcept 462 | { 463 | Enum e; 464 | do { 465 | if (hash(e.value->BaseDllName) == Hash) 466 | return (T)(e.value->DllBase); 467 | } while (e.next()); 468 | return {}; 469 | } 470 | }; 471 | 472 | template 473 | struct lazy_function : lazy_base, T> { 474 | using base_type = lazy_base, T>; 475 | 476 | template 477 | LAZY_IMPORTER_FORCEINLINE decltype(auto) operator()(Args&&... args) const 478 | { 479 | #ifndef LAZY_IMPORTER_CACHE_OPERATOR_PARENS 480 | return get()(std::forward(args)...); 481 | #else 482 | return cached()(std::forward(args)...); 483 | #endif 484 | } 485 | 486 | template 487 | LAZY_IMPORTER_FORCEINLINE static F get() noexcept 488 | { 489 | // for backwards compatability. 490 | // Before 2.0 it was only possible to resolve forwarded exports when 491 | // this macro was enabled 492 | #ifdef LAZY_IMPORTER_RESOLVE_FORWARDED_EXPORTS 493 | return forwarded(); 494 | #else 495 | Enum e; 496 | do { 497 | const exports_directory exports(e.value->DllBase); 498 | 499 | if (exports) { 500 | auto export_index = exports.size(); 501 | while (export_index--) 502 | if (hash(exports.name(export_index)) == Hash) 503 | return (F)(exports.address(export_index)); 504 | } 505 | } while (e.next()); 506 | return {}; 507 | #endif 508 | } 509 | 510 | template 511 | LAZY_IMPORTER_FORCEINLINE static F forwarded() noexcept 512 | { 513 | detail::win::UNICODE_STRING_T name; 514 | hash_t::value_type module_hash = 0; 515 | auto function_hash = Hash; 516 | 517 | Enum e; 518 | do { 519 | name = e.value->BaseDllName; 520 | name.Length -= 8; // get rid of .dll extension 521 | 522 | if (!module_hash || hash(name) == module_hash) { 523 | const exports_directory exports(e.value->DllBase); 524 | 525 | if (exports) { 526 | auto export_index = exports.size(); 527 | while (export_index--) 528 | if (hash(exports.name(export_index)) == function_hash) { 529 | const auto addr = exports.address(export_index); 530 | 531 | if (exports.is_forwarded(addr)) { 532 | auto hashes = hash_forwarded( 533 | reinterpret_cast(addr)); 534 | 535 | function_hash = hashes.second; 536 | module_hash = hashes.first; 537 | 538 | e.reset(); 539 | break; 540 | } 541 | return (F)(addr); 542 | } 543 | } 544 | } 545 | } while (e.next()); 546 | return {}; 547 | } 548 | 549 | template 550 | LAZY_IMPORTER_FORCEINLINE static F forwarded_safe() noexcept 551 | { 552 | return forwarded(); 553 | } 554 | 555 | template 556 | LAZY_IMPORTER_FORCEINLINE static F forwarded_cached() noexcept 557 | { 558 | auto& value = base_type::_cache(); 559 | if (!value) 560 | value = forwarded(); 561 | return (F)(value); 562 | } 563 | 564 | template 565 | LAZY_IMPORTER_FORCEINLINE static F forwarded_safe_cached() noexcept 566 | { 567 | return forwarded_cached(); 568 | } 569 | 570 | template 571 | LAZY_IMPORTER_FORCEINLINE static F in(Module m) noexcept 572 | { 573 | if (IsSafe && !m) 574 | return {}; 575 | 576 | const exports_directory exports((const char*)(m)); 577 | if (IsSafe && !exports) 578 | return {}; 579 | 580 | for (unsigned long i{};; ++i) { 581 | if (IsSafe && i == exports.size()) 582 | break; 583 | 584 | if (hash(exports.name(i)) == Hash) 585 | return (F)(exports.address(i)); 586 | } 587 | return {}; 588 | } 589 | 590 | template 591 | LAZY_IMPORTER_FORCEINLINE static F in_safe(Module m) noexcept 592 | { 593 | return in(m); 594 | } 595 | 596 | template 597 | LAZY_IMPORTER_FORCEINLINE static F in_cached(Module m) noexcept 598 | { 599 | auto& value = base_type::_cache(); 600 | if (!value) 601 | value = in(m); 602 | return (F)(value); 603 | } 604 | 605 | template 606 | LAZY_IMPORTER_FORCEINLINE static F in_safe_cached(Module m) noexcept 607 | { 608 | return in_cached(m); 609 | } 610 | 611 | template 612 | LAZY_IMPORTER_FORCEINLINE static F nt() noexcept 613 | { 614 | return in(ldr_data_entry()->load_order_next()->DllBase); 615 | } 616 | 617 | template 618 | LAZY_IMPORTER_FORCEINLINE static F nt_safe() noexcept 619 | { 620 | return in_safe(ldr_data_entry()->load_order_next()->DllBase); 621 | } 622 | 623 | template 624 | LAZY_IMPORTER_FORCEINLINE static F nt_cached() noexcept 625 | { 626 | return in_cached(ldr_data_entry()->load_order_next()->DllBase); 627 | } 628 | 629 | template 630 | LAZY_IMPORTER_FORCEINLINE static F nt_safe_cached() noexcept 631 | { 632 | return in_safe_cached(ldr_data_entry()->load_order_next()->DllBase); 633 | } 634 | }; 635 | 636 | } 637 | } // namespace li::detail 638 | 639 | #endif // include guard -------------------------------------------------------------------------------- /gui/imgui_impl_dx11.cpp: -------------------------------------------------------------------------------- 1 | 2 | // ImGui Win32 + DirectX11 binding 3 | // In this binding, ImTextureID is used to store a 'ID3D11ShaderResourceView*' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. 4 | 5 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 6 | // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). 7 | // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. 8 | // https://github.com/ocornut/imgui 9 | 10 | #include "imgui.h" 11 | #include "imgui_impl_dx11.h" 12 | #include "imgui_internal.h" 13 | 14 | // DirectX 15 | #include 16 | #include 17 | #define DIRECTINPUT_VERSION 0x0800 18 | #include 19 | 20 | #pragma comment(lib,"d3dcompiler.lib") 21 | 22 | // Data 23 | static INT64 g_Time = 0; 24 | static INT64 g_TicksPerSecond = 0; 25 | 26 | static HWND g_hWnd = 0; 27 | static ID3D11Device* g_pd3dDevice = NULL; 28 | static ID3D11DeviceContext* g_pd3dDeviceContext = NULL; 29 | static ID3D11Buffer* g_pVB = NULL; 30 | static ID3D11Buffer* g_pIB = NULL; 31 | static ID3D10Blob * g_pVertexShaderBlob = NULL; 32 | static ID3D11VertexShader* g_pVertexShader = NULL; 33 | static ID3D11InputLayout* g_pInputLayout = NULL; 34 | static ID3D11Buffer* g_pVertexConstantBuffer = NULL; 35 | static ID3D10Blob * g_pPixelShaderBlob = NULL; 36 | static ID3D11PixelShader* g_pPixelShader = NULL; 37 | static ID3D11SamplerState* g_pFontSampler = NULL; 38 | static ID3D11ShaderResourceView*g_pFontTextureView = NULL; 39 | static ID3D11RasterizerState* g_pRasterizerState = NULL; 40 | static ID3D11BlendState* g_pBlendState = NULL; 41 | static ID3D11DepthStencilState* g_pDepthStencilState = NULL; 42 | static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; 43 | 44 | struct VERTEX_CONSTANT_BUFFER 45 | { 46 | float mvp[4][4]; 47 | }; 48 | 49 | // This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure) 50 | // If text or lines are blurry when integrating ImGui in your engine: 51 | // - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f) 52 | void ImGui_ImplDX11_RenderDrawLists(ImDrawData* draw_data) 53 | { 54 | ID3D11DeviceContext* ctx = g_pd3dDeviceContext; 55 | 56 | // Create and grow vertex/index buffers if needed 57 | if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) 58 | { 59 | if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } 60 | g_VertexBufferSize = draw_data->TotalVtxCount + 5000; 61 | D3D11_BUFFER_DESC desc; 62 | memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); 63 | desc.Usage = D3D11_USAGE_DYNAMIC; 64 | desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert); 65 | desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; 66 | desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 67 | desc.MiscFlags = 0; 68 | if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0) 69 | return; 70 | } 71 | if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) 72 | { 73 | if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } 74 | g_IndexBufferSize = draw_data->TotalIdxCount + 10000; 75 | D3D11_BUFFER_DESC desc; 76 | memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); 77 | desc.Usage = D3D11_USAGE_DYNAMIC; 78 | desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx); 79 | desc.BindFlags = D3D11_BIND_INDEX_BUFFER; 80 | desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 81 | if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pIB) < 0) 82 | return; 83 | } 84 | 85 | // Copy and convert all vertices into a single contiguous buffer 86 | D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource; 87 | if (ctx->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK) 88 | return; 89 | if (ctx->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) 90 | return; 91 | ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData; 92 | ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData; 93 | for (int n = 0; n < draw_data->CmdListsCount; n++) 94 | { 95 | const ImDrawList* cmd_list = draw_data->CmdLists[n]; 96 | memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); 97 | memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); 98 | vtx_dst += cmd_list->VtxBuffer.Size; 99 | idx_dst += cmd_list->IdxBuffer.Size; 100 | } 101 | ctx->Unmap(g_pVB, 0); 102 | ctx->Unmap(g_pIB, 0); 103 | 104 | // Setup orthographic projection matrix into our constant buffer 105 | { 106 | D3D11_MAPPED_SUBRESOURCE mapped_resource; 107 | if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) 108 | return; 109 | VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData; 110 | float L = 0.0f; 111 | float R = ImGui::GetIO().DisplaySize.x; 112 | float B = ImGui::GetIO().DisplaySize.y; 113 | float T = 0.0f; 114 | float mvp[4][4] = 115 | { 116 | { 2.0f / (R - L), 0.0f, 0.0f, 0.0f }, 117 | { 0.0f, 2.0f / (T - B), 0.0f, 0.0f }, 118 | { 0.0f, 0.0f, 0.5f, 0.0f }, 119 | { (R + L) / (L - R), (T + B) / (B - T), 0.5f, 1.0f }, 120 | }; 121 | memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); 122 | ctx->Unmap(g_pVertexConstantBuffer, 0); 123 | } 124 | 125 | // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!) 126 | struct BACKUP_DX11_STATE 127 | { 128 | UINT ScissorRectsCount, ViewportsCount; 129 | D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; 130 | D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; 131 | ID3D11RasterizerState* RS; 132 | ID3D11BlendState* BlendState; 133 | FLOAT BlendFactor[4]; 134 | UINT SampleMask; 135 | UINT StencilRef; 136 | ID3D11DepthStencilState* DepthStencilState; 137 | ID3D11ShaderResourceView* PSShaderResource; 138 | ID3D11SamplerState* PSSampler; 139 | ID3D11PixelShader* PS; 140 | ID3D11VertexShader* VS; 141 | UINT PSInstancesCount, VSInstancesCount; 142 | ID3D11ClassInstance* PSInstances[256], *VSInstances[256]; // 256 is max according to PSSetShader documentation 143 | D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology; 144 | ID3D11Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer; 145 | UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset; 146 | DXGI_FORMAT IndexBufferFormat; 147 | ID3D11InputLayout* InputLayout; 148 | }; 149 | BACKUP_DX11_STATE old; 150 | old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; 151 | ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects); 152 | ctx->RSGetViewports(&old.ViewportsCount, old.Viewports); 153 | ctx->RSGetState(&old.RS); 154 | ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask); 155 | ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef); 156 | ctx->PSGetShaderResources(0, 1, &old.PSShaderResource); 157 | ctx->PSGetSamplers(0, 1, &old.PSSampler); 158 | old.PSInstancesCount = old.VSInstancesCount = 256; 159 | ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount); 160 | ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount); 161 | ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer); 162 | ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology); 163 | ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset); 164 | ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); 165 | ctx->IAGetInputLayout(&old.InputLayout); 166 | 167 | // Setup viewport 168 | D3D11_VIEWPORT vp; 169 | memset(&vp, 0, sizeof(D3D11_VIEWPORT)); 170 | vp.Width = ImGui::GetIO().DisplaySize.x; 171 | vp.Height = ImGui::GetIO().DisplaySize.y; 172 | vp.MinDepth = 0.0f; 173 | vp.MaxDepth = 1.0f; 174 | vp.TopLeftX = vp.TopLeftY = 0.0f; 175 | ctx->RSSetViewports(1, &vp); 176 | 177 | // Bind shader and vertex buffers 178 | unsigned int stride = sizeof(ImDrawVert); 179 | unsigned int offset = 0; 180 | ctx->IASetInputLayout(g_pInputLayout); 181 | ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset); 182 | ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); 183 | ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 184 | ctx->VSSetShader(g_pVertexShader, NULL, 0); 185 | ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer); 186 | ctx->PSSetShader(g_pPixelShader, NULL, 0); 187 | ctx->PSSetSamplers(0, 1, &g_pFontSampler); 188 | 189 | // Setup render state 190 | const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; 191 | ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff); 192 | ctx->OMSetDepthStencilState(g_pDepthStencilState, 0); 193 | ctx->RSSetState(g_pRasterizerState); 194 | 195 | // Render command lists 196 | int vtx_offset = 0; 197 | int idx_offset = 0; 198 | for (int n = 0; n < draw_data->CmdListsCount; n++) 199 | { 200 | const ImDrawList* cmd_list = draw_data->CmdLists[n]; 201 | for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) 202 | { 203 | const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; 204 | if (pcmd->UserCallback) 205 | { 206 | pcmd->UserCallback(cmd_list, pcmd); 207 | } 208 | else 209 | { 210 | const D3D11_RECT r = { (LONG)pcmd->ClipRect.x, (LONG)pcmd->ClipRect.y, (LONG)pcmd->ClipRect.z, (LONG)pcmd->ClipRect.w }; 211 | ctx->PSSetShaderResources(0, 1, (ID3D11ShaderResourceView**)&pcmd->TextureId); 212 | ctx->RSSetScissorRects(1, &r); 213 | ctx->DrawIndexed(pcmd->ElemCount, idx_offset, vtx_offset); 214 | } 215 | idx_offset += pcmd->ElemCount; 216 | } 217 | vtx_offset += cmd_list->VtxBuffer.Size; 218 | } 219 | 220 | // Restore modified DX state 221 | ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects); 222 | ctx->RSSetViewports(old.ViewportsCount, old.Viewports); 223 | ctx->RSSetState(old.RS); if (old.RS) old.RS->Release(); 224 | ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release(); 225 | ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release(); 226 | ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release(); 227 | ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release(); 228 | ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release(); 229 | for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release(); 230 | ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release(); 231 | ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release(); 232 | for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release(); 233 | ctx->IASetPrimitiveTopology(old.PrimitiveTopology); 234 | ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release(); 235 | ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release(); 236 | ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); 237 | } 238 | 239 | static bool IsAnyMouseButtonDown() 240 | { 241 | ImGuiIO& io = ImGui::GetIO(); 242 | for (int n = 0; n < IM_ARRAYSIZE(io.MouseDown); n++) 243 | if (io.MouseDown[n]) 244 | return true; 245 | return false; 246 | } 247 | 248 | // Process Win32 mouse/keyboard inputs. 249 | // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. 250 | // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. 251 | // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. 252 | // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. 253 | // PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinations when dragging mouse outside of our window bounds. 254 | // PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag. 255 | IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 256 | { 257 | ImGuiIO& io = ImGui::GetIO(); 258 | switch (msg) 259 | { 260 | case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: 261 | case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: 262 | case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: 263 | { 264 | int button = 0; 265 | if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) button = 0; 266 | if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) button = 1; 267 | if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) button = 2; 268 | if (!IsAnyMouseButtonDown() && GetCapture() == NULL) 269 | SetCapture(hwnd); 270 | io.MouseDown[button] = true; 271 | return 0; 272 | } 273 | case WM_LBUTTONUP: 274 | case WM_RBUTTONUP: 275 | case WM_MBUTTONUP: 276 | { 277 | int button = 0; 278 | if (msg == WM_LBUTTONUP) button = 0; 279 | if (msg == WM_RBUTTONUP) button = 1; 280 | if (msg == WM_MBUTTONUP) button = 2; 281 | io.MouseDown[button] = false; 282 | if (!IsAnyMouseButtonDown() && GetCapture() == hwnd) 283 | ReleaseCapture(); 284 | return 0; 285 | } 286 | case WM_MOUSEWHEEL: 287 | io.MouseWheel += GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? +1.0f : -1.0f; 288 | return 0; 289 | case WM_MOUSEMOVE: 290 | io.MousePos.x = (signed short)(lParam); 291 | io.MousePos.y = (signed short)(lParam >> 16); 292 | return 0; 293 | case WM_KEYDOWN: 294 | case WM_SYSKEYDOWN: 295 | if (wParam < 256) 296 | io.KeysDown[wParam] = 1; 297 | return 0; 298 | case WM_KEYUP: 299 | case WM_SYSKEYUP: 300 | if (wParam < 256) 301 | io.KeysDown[wParam] = 0; 302 | return 0; 303 | case WM_CHAR: 304 | // You can also use ToAscii()+GetKeyboardState() to retrieve characters. 305 | if (wParam > 0 && wParam < 0x10000) 306 | io.AddInputCharacter((unsigned short)wParam); 307 | return 0; 308 | } 309 | return 0; 310 | } 311 | 312 | static void ImGui_ImplDX11_CreateFontsTexture() 313 | { 314 | // Build texture atlas 315 | ImGuiIO& io = ImGui::GetIO(); 316 | unsigned char* pixels; 317 | int width, height; 318 | io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); 319 | 320 | // Upload texture to graphics system 321 | { 322 | D3D11_TEXTURE2D_DESC desc; 323 | ZeroMemory(&desc, sizeof(desc)); 324 | desc.Width = width; 325 | desc.Height = height; 326 | desc.MipLevels = 1; 327 | desc.ArraySize = 1; 328 | desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 329 | desc.SampleDesc.Count = 1; 330 | desc.Usage = D3D11_USAGE_DEFAULT; 331 | desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; 332 | desc.CPUAccessFlags = 0; 333 | 334 | ID3D11Texture2D *pTexture = NULL; 335 | D3D11_SUBRESOURCE_DATA subResource; 336 | subResource.pSysMem = pixels; 337 | subResource.SysMemPitch = desc.Width * 4; 338 | subResource.SysMemSlicePitch = 0; 339 | g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); 340 | 341 | // Create texture view 342 | D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; 343 | ZeroMemory(&srvDesc, sizeof(srvDesc)); 344 | srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 345 | srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; 346 | srvDesc.Texture2D.MipLevels = desc.MipLevels; 347 | srvDesc.Texture2D.MostDetailedMip = 0; 348 | g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView); 349 | pTexture->Release(); 350 | } 351 | 352 | // Store our identifier 353 | io.Fonts->TexID = (void *)g_pFontTextureView; 354 | 355 | // Create texture sampler 356 | { 357 | D3D11_SAMPLER_DESC desc; 358 | ZeroMemory(&desc, sizeof(desc)); 359 | desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; 360 | desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; 361 | desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; 362 | desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; 363 | desc.MipLODBias = 0.f; 364 | desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; 365 | desc.MinLOD = 0.f; 366 | desc.MaxLOD = 0.f; 367 | g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler); 368 | } 369 | } 370 | 371 | bool ImGui_ImplDX11_CreateDeviceObjects() 372 | { 373 | if (!g_pd3dDevice) 374 | return false; 375 | if (g_pFontSampler) 376 | ImGui_ImplDX11_InvalidateDeviceObjects(); 377 | 378 | // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) 379 | // If you would like to use this DX11 sample code but remove this dependency you can: 380 | // 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution] 381 | // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL. 382 | // See https://github.com/ocornut/imgui/pull/638 for sources and details. 383 | 384 | // Create the vertex shader 385 | { 386 | static const char* vertexShader = 387 | "cbuffer vertexBuffer : register(b0) \ 388 | {\ 389 | float4x4 ProjectionMatrix; \ 390 | };\ 391 | struct VS_INPUT\ 392 | {\ 393 | float2 pos : POSITION;\ 394 | float4 col : COLOR0;\ 395 | float2 uv : TEXCOORD0;\ 396 | };\ 397 | \ 398 | struct PS_INPUT\ 399 | {\ 400 | float4 pos : SV_POSITION;\ 401 | float4 col : COLOR0;\ 402 | float2 uv : TEXCOORD0;\ 403 | };\ 404 | \ 405 | PS_INPUT main(VS_INPUT input)\ 406 | {\ 407 | PS_INPUT output;\ 408 | output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\ 409 | output.col = input.col;\ 410 | output.uv = input.uv;\ 411 | return output;\ 412 | }"; 413 | 414 | D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &g_pVertexShaderBlob, NULL); 415 | if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! 416 | return false; 417 | if (g_pd3dDevice->CreateVertexShader((DWORD*)g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), NULL, &g_pVertexShader) != S_OK) 418 | return false; 419 | 420 | // Create the input layout 421 | D3D11_INPUT_ELEMENT_DESC local_layout[] = { 422 | { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->pos), D3D11_INPUT_PER_VERTEX_DATA, 0 }, 423 | { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (size_t)(&((ImDrawVert*)0)->uv), D3D11_INPUT_PER_VERTEX_DATA, 0 }, 424 | { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (size_t)(&((ImDrawVert*)0)->col), D3D11_INPUT_PER_VERTEX_DATA, 0 }, 425 | }; 426 | if (g_pd3dDevice->CreateInputLayout(local_layout, 3, g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK) 427 | return false; 428 | 429 | // Create the constant buffer 430 | { 431 | D3D11_BUFFER_DESC desc; 432 | desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER); 433 | desc.Usage = D3D11_USAGE_DYNAMIC; 434 | desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; 435 | desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; 436 | desc.MiscFlags = 0; 437 | g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer); 438 | } 439 | } 440 | 441 | // Create the pixel shader 442 | { 443 | static const char* pixelShader = 444 | "struct PS_INPUT\ 445 | {\ 446 | float4 pos : SV_POSITION;\ 447 | float4 col : COLOR0;\ 448 | float2 uv : TEXCOORD0;\ 449 | };\ 450 | sampler sampler0;\ 451 | Texture2D texture0;\ 452 | \ 453 | float4 main(PS_INPUT input) : SV_Target\ 454 | {\ 455 | float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \ 456 | return out_col; \ 457 | }"; 458 | 459 | D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &g_pPixelShaderBlob, NULL); 460 | if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! 461 | return false; 462 | if (g_pd3dDevice->CreatePixelShader((DWORD*)g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize(), NULL, &g_pPixelShader) != S_OK) 463 | return false; 464 | } 465 | 466 | // Create the blending setup 467 | { 468 | D3D11_BLEND_DESC desc; 469 | ZeroMemory(&desc, sizeof(desc)); 470 | desc.AlphaToCoverageEnable = false; 471 | desc.RenderTarget[0].BlendEnable = true; 472 | desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; 473 | desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; 474 | desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; 475 | desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; 476 | desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; 477 | desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; 478 | desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; 479 | g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState); 480 | } 481 | 482 | // Create the rasterizer state 483 | { 484 | D3D11_RASTERIZER_DESC desc; 485 | ZeroMemory(&desc, sizeof(desc)); 486 | desc.FillMode = D3D11_FILL_SOLID; 487 | desc.CullMode = D3D11_CULL_NONE; 488 | desc.ScissorEnable = true; 489 | desc.DepthClipEnable = true; 490 | g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState); 491 | } 492 | 493 | // Create depth-stencil State 494 | { 495 | D3D11_DEPTH_STENCIL_DESC desc; 496 | ZeroMemory(&desc, sizeof(desc)); 497 | desc.DepthEnable = false; 498 | desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; 499 | desc.DepthFunc = D3D11_COMPARISON_ALWAYS; 500 | desc.StencilEnable = false; 501 | desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; 502 | desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; 503 | desc.BackFace = desc.FrontFace; 504 | g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState); 505 | } 506 | 507 | ImGui_ImplDX11_CreateFontsTexture(); 508 | 509 | return true; 510 | } 511 | 512 | void ImGui_ImplDX11_InvalidateDeviceObjects() 513 | { 514 | if (!g_pd3dDevice) 515 | return; 516 | 517 | if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; } 518 | if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. 519 | if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } 520 | if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } 521 | 522 | if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; } 523 | if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; } 524 | if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; } 525 | if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; } 526 | if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; } 527 | if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; } 528 | if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; } 529 | if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; } 530 | if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; } 531 | } 532 | 533 | bool ImGui_ImplDX11_Init(void* hwnd, ID3D11Device* device, ID3D11DeviceContext* device_context) 534 | { 535 | g_hWnd = (HWND)hwnd; 536 | g_pd3dDevice = device; 537 | g_pd3dDeviceContext = device_context; 538 | 539 | if (!QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond)) 540 | return false; 541 | if (!QueryPerformanceCounter((LARGE_INTEGER *)&g_Time)) 542 | return false; 543 | 544 | ImGuiIO& io = ImGui::GetIO(); 545 | io.KeyMap[ImGuiKey_Tab] = VK_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array that we will update during the application lifetime. 546 | io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; 547 | io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; 548 | io.KeyMap[ImGuiKey_UpArrow] = VK_UP; 549 | io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN; 550 | io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR; 551 | io.KeyMap[ImGuiKey_PageDown] = VK_NEXT; 552 | io.KeyMap[ImGuiKey_Home] = VK_HOME; 553 | io.KeyMap[ImGuiKey_End] = VK_END; 554 | io.KeyMap[ImGuiKey_Insert] = VK_INSERT; 555 | io.KeyMap[ImGuiKey_Delete] = VK_DELETE; 556 | io.KeyMap[ImGuiKey_Backspace] = VK_BACK; 557 | io.KeyMap[ImGuiKey_Enter] = VK_RETURN; 558 | io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; 559 | io.KeyMap[ImGuiKey_A] = 'A'; 560 | io.KeyMap[ImGuiKey_C] = 'C'; 561 | io.KeyMap[ImGuiKey_V] = 'V'; 562 | io.KeyMap[ImGuiKey_X] = 'X'; 563 | io.KeyMap[ImGuiKey_Y] = 'Y'; 564 | io.KeyMap[ImGuiKey_Z] = 'Z'; 565 | 566 | io.RenderDrawListsFn = ImGui_ImplDX11_RenderDrawLists; // Alternatively you can set this to NULL and call ImGui::GetDrawData() after ImGui::Render() to get the same ImDrawData pointer. 567 | io.ImeWindowHandle = g_hWnd; 568 | 569 | return true; 570 | } 571 | 572 | void ImGui_ImplDX11_Shutdown() 573 | { 574 | ImGui_ImplDX11_InvalidateDeviceObjects(); 575 | ImGui::Shutdown(); 576 | g_pd3dDevice = NULL; 577 | g_pd3dDeviceContext = NULL; 578 | g_hWnd = (HWND)0; 579 | } 580 | 581 | void ImGui_ImplDX11_NewFrame() 582 | { 583 | if (!g_pFontSampler) 584 | ImGui_ImplDX11_CreateDeviceObjects(); 585 | 586 | ImGuiIO& io = ImGui::GetIO(); 587 | 588 | // Setup display size (every frame to accommodate for window resizing) 589 | RECT rect; 590 | GetClientRect(g_hWnd, &rect); 591 | io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); 592 | 593 | // Setup time step 594 | INT64 current_time; 595 | QueryPerformanceCounter((LARGE_INTEGER *)¤t_time); 596 | io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; 597 | g_Time = current_time; 598 | 599 | // Read keyboard modifiers inputs 600 | io.KeyCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; 601 | io.KeyShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; 602 | io.KeyAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; 603 | io.KeySuper = false; 604 | // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events 605 | // io.MousePos : filled by WM_MOUSEMOVE events 606 | // io.MouseDown : filled by WM_*BUTTON* events 607 | // io.MouseWheel : filled by WM_MOUSEWHEEL events 608 | 609 | // Set OS mouse position if requested last frame by io.WantMoveMouse flag (used when io.NavMovesTrue is enabled by user and using directional navigation) 610 | if (io.WantMoveMouse) 611 | { 612 | POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; 613 | ClientToScreen(g_hWnd, &pos); 614 | SetCursorPos(pos.x, pos.y); 615 | } 616 | 617 | // Hide OS mouse cursor if ImGui is drawing it 618 | if (io.MouseDrawCursor) 619 | SetCursor(NULL); 620 | 621 | // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. 622 | ImGui::NewFrame(); 623 | } 624 | -------------------------------------------------------------------------------- /gui/stb_textedit.h: -------------------------------------------------------------------------------- 1 | // [ImGui] this is a slightly modified version of stb_truetype.h 1.9. Those changes would need to be pushed into nothings/sb 2 | // [ImGui] - fixed linestart handler when over last character of multi-line buffer + simplified existing code (#588, #815) 3 | // [ImGui] - fixed a state corruption/crash bug in stb_text_redo and stb_textedit_discard_redo (#715) 4 | // [ImGui] - fixed a crash bug in stb_textedit_discard_redo (#681) 5 | // [ImGui] - fixed some minor warnings 6 | 7 | // stb_textedit.h - v1.9 - public domain - Sean Barrett 8 | // Development of this library was sponsored by RAD Game Tools 9 | // 10 | // This C header file implements the guts of a multi-line text-editing 11 | // widget; you implement display, word-wrapping, and low-level string 12 | // insertion/deletion, and stb_textedit will map user inputs into 13 | // insertions & deletions, plus updates to the cursor position, 14 | // selection state, and undo state. 15 | // 16 | // It is intended for use in games and other systems that need to build 17 | // their own custom widgets and which do not have heavy text-editing 18 | // requirements (this library is not recommended for use for editing large 19 | // texts, as its performance does not scale and it has limited undo). 20 | // 21 | // Non-trivial behaviors are modelled after Windows text controls. 22 | // 23 | // 24 | // LICENSE 25 | // 26 | // This software is dual-licensed to the public domain and under the following 27 | // license: you are granted a perpetual, irrevocable license to copy, modify, 28 | // publish, and distribute this file as you see fit. 29 | // 30 | // 31 | // DEPENDENCIES 32 | // 33 | // Uses the C runtime function 'memmove', which you can override 34 | // by defining STB_TEXTEDIT_memmove before the implementation. 35 | // Uses no other functions. Performs no runtime allocations. 36 | // 37 | // 38 | // VERSION HISTORY 39 | // 40 | // 1.9 (2016-08-27) customizable move-by-word 41 | // 1.8 (2016-04-02) better keyboard handling when mouse button is down 42 | // 1.7 (2015-09-13) change y range handling in case baseline is non-0 43 | // 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove 44 | // 1.5 (2014-09-10) add support for secondary keys for OS X 45 | // 1.4 (2014-08-17) fix signed/unsigned warnings 46 | // 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary 47 | // 1.2 (2014-05-27) fix some RAD types that had crept into the new code 48 | // 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE ) 49 | // 1.0 (2012-07-26) improve documentation, initial public release 50 | // 0.3 (2012-02-24) bugfixes, single-line mode; insert mode 51 | // 0.2 (2011-11-28) fixes to undo/redo 52 | // 0.1 (2010-07-08) initial version 53 | // 54 | // ADDITIONAL CONTRIBUTORS 55 | // 56 | // Ulf Winklemann: move-by-word in 1.1 57 | // Fabian Giesen: secondary key inputs in 1.5 58 | // Martins Mozeiko: STB_TEXTEDIT_memmove 59 | // 60 | // Bugfixes: 61 | // Scott Graham 62 | // Daniel Keller 63 | // Omar Cornut 64 | // 65 | // USAGE 66 | // 67 | // This file behaves differently depending on what symbols you define 68 | // before including it. 69 | // 70 | // 71 | // Header-file mode: 72 | // 73 | // If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this, 74 | // it will operate in "header file" mode. In this mode, it declares a 75 | // single public symbol, STB_TexteditState, which encapsulates the current 76 | // state of a text widget (except for the string, which you will store 77 | // separately). 78 | // 79 | // To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a 80 | // primitive type that defines a single character (e.g. char, wchar_t, etc). 81 | // 82 | // To save space or increase undo-ability, you can optionally define the 83 | // following things that are used by the undo system: 84 | // 85 | // STB_TEXTEDIT_POSITIONTYPE small int type encoding a valid cursor position 86 | // STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow 87 | // STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer 88 | // 89 | // If you don't define these, they are set to permissive types and 90 | // moderate sizes. The undo system does no memory allocations, so 91 | // it grows STB_TexteditState by the worst-case storage which is (in bytes): 92 | // 93 | // [4 + sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT 94 | // + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT 95 | // 96 | // 97 | // Implementation mode: 98 | // 99 | // If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it 100 | // will compile the implementation of the text edit widget, depending 101 | // on a large number of symbols which must be defined before the include. 102 | // 103 | // The implementation is defined only as static functions. You will then 104 | // need to provide your own APIs in the same file which will access the 105 | // static functions. 106 | // 107 | // The basic concept is that you provide a "string" object which 108 | // behaves like an array of characters. stb_textedit uses indices to 109 | // refer to positions in the string, implicitly representing positions 110 | // in the displayed textedit. This is true for both plain text and 111 | // rich text; even with rich text stb_truetype interacts with your 112 | // code as if there was an array of all the displayed characters. 113 | // 114 | // Symbols that must be the same in header-file and implementation mode: 115 | // 116 | // STB_TEXTEDIT_CHARTYPE the character type 117 | // STB_TEXTEDIT_POSITIONTYPE small type that a valid cursor position 118 | // STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow 119 | // STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer 120 | // 121 | // Symbols you must define for implementation mode: 122 | // 123 | // STB_TEXTEDIT_STRING the type of object representing a string being edited, 124 | // typically this is a wrapper object with other data you need 125 | // 126 | // STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1)) 127 | // STB_TEXTEDIT_LAYOUTROW(&r,obj,n) returns the results of laying out a line of characters 128 | // starting from character #n (see discussion below) 129 | // STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character 130 | // to the xpos of the i+1'th char for a line of characters 131 | // starting at character #n (i.e. accounts for kerning 132 | // with previous char) 133 | // STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character 134 | // (return type is int, -1 means not valid to insert) 135 | // STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based 136 | // STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize 137 | // as manually wordwrapping for end-of-line positioning 138 | // 139 | // STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i 140 | // STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) 141 | // 142 | // STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key 143 | // 144 | // STB_TEXTEDIT_K_LEFT keyboard input to move cursor left 145 | // STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right 146 | // STB_TEXTEDIT_K_UP keyboard input to move cursor up 147 | // STB_TEXTEDIT_K_DOWN keyboard input to move cursor down 148 | // STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME 149 | // STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END 150 | // STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME 151 | // STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text // e.g. ctrl-END 152 | // STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor 153 | // STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor 154 | // STB_TEXTEDIT_K_UNDO keyboard input to perform undo 155 | // STB_TEXTEDIT_K_REDO keyboard input to perform redo 156 | // 157 | // Optional: 158 | // STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode 159 | // STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), 160 | // required for default WORDLEFT/WORDRIGHT handlers 161 | // STB_TEXTEDIT_MOVEWORDLEFT(obj,i) custom handler for WORDLEFT, returns index to move cursor to 162 | // STB_TEXTEDIT_MOVEWORDRIGHT(obj,i) custom handler for WORDRIGHT, returns index to move cursor to 163 | // STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT 164 | // STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT 165 | // STB_TEXTEDIT_K_LINESTART2 secondary keyboard input to move cursor to start of line 166 | // STB_TEXTEDIT_K_LINEEND2 secondary keyboard input to move cursor to end of line 167 | // STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text 168 | // STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text 169 | // 170 | // Todo: 171 | // STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page 172 | // STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page 173 | // 174 | // Keyboard input must be encoded as a single integer value; e.g. a character code 175 | // and some bitflags that represent shift states. to simplify the interface, SHIFT must 176 | // be a bitflag, so we can test the shifted state of cursor movements to allow selection, 177 | // i.e. (STB_TEXTED_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow. 178 | // 179 | // You can encode other things, such as CONTROL or ALT, in additional bits, and 180 | // then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example, 181 | // my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN 182 | // bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit, 183 | // and I pass both WM_KEYDOWN and WM_CHAR events to the "key" function in the 184 | // API below. The control keys will only match WM_KEYDOWN events because of the 185 | // keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN 186 | // bit so it only decodes WM_CHAR events. 187 | // 188 | // STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed 189 | // row of characters assuming they start on the i'th character--the width and 190 | // the height and the number of characters consumed. This allows this library 191 | // to traverse the entire layout incrementally. You need to compute word-wrapping 192 | // here. 193 | // 194 | // Each textfield keeps its own insert mode state, which is not how normal 195 | // applications work. To keep an app-wide insert mode, update/copy the 196 | // "insert_mode" field of STB_TexteditState before/after calling API functions. 197 | // 198 | // API 199 | // 200 | // void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) 201 | // 202 | // void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 203 | // void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 204 | // int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 205 | // int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) 206 | // void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) 207 | // 208 | // Each of these functions potentially updates the string and updates the 209 | // state. 210 | // 211 | // initialize_state: 212 | // set the textedit state to a known good default state when initially 213 | // constructing the textedit. 214 | // 215 | // click: 216 | // call this with the mouse x,y on a mouse down; it will update the cursor 217 | // and reset the selection start/end to the cursor point. the x,y must 218 | // be relative to the text widget, with (0,0) being the top left. 219 | // 220 | // drag: 221 | // call this with the mouse x,y on a mouse drag/up; it will update the 222 | // cursor and the selection end point 223 | // 224 | // cut: 225 | // call this to delete the current selection; returns true if there was 226 | // one. you should FIRST copy the current selection to the system paste buffer. 227 | // (To copy, just copy the current selection out of the string yourself.) 228 | // 229 | // paste: 230 | // call this to paste text at the current cursor point or over the current 231 | // selection if there is one. 232 | // 233 | // key: 234 | // call this for keyboard inputs sent to the textfield. you can use it 235 | // for "key down" events or for "translated" key events. if you need to 236 | // do both (as in Win32), or distinguish Unicode characters from control 237 | // inputs, set a high bit to distinguish the two; then you can define the 238 | // various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit 239 | // set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is 240 | // clear. 241 | // 242 | // When rendering, you can read the cursor position and selection state from 243 | // the STB_TexteditState. 244 | // 245 | // 246 | // Notes: 247 | // 248 | // This is designed to be usable in IMGUI, so it allows for the possibility of 249 | // running in an IMGUI that has NOT cached the multi-line layout. For this 250 | // reason, it provides an interface that is compatible with computing the 251 | // layout incrementally--we try to make sure we make as few passes through 252 | // as possible. (For example, to locate the mouse pointer in the text, we 253 | // could define functions that return the X and Y positions of characters 254 | // and binary search Y and then X, but if we're doing dynamic layout this 255 | // will run the layout algorithm many times, so instead we manually search 256 | // forward in one pass. Similar logic applies to e.g. up-arrow and 257 | // down-arrow movement.) 258 | // 259 | // If it's run in a widget that *has* cached the layout, then this is less 260 | // efficient, but it's not horrible on modern computers. But you wouldn't 261 | // want to edit million-line files with it. 262 | 263 | 264 | //////////////////////////////////////////////////////////////////////////// 265 | //////////////////////////////////////////////////////////////////////////// 266 | //// 267 | //// Header-file mode 268 | //// 269 | //// 270 | 271 | #ifndef INCLUDE_STB_TEXTEDIT_H 272 | #define INCLUDE_STB_TEXTEDIT_H 273 | 274 | //////////////////////////////////////////////////////////////////////// 275 | // 276 | // STB_TexteditState 277 | // 278 | // Definition of STB_TexteditState which you should store 279 | // per-textfield; it includes cursor position, selection state, 280 | // and undo state. 281 | // 282 | 283 | #ifndef STB_TEXTEDIT_UNDOSTATECOUNT 284 | #define STB_TEXTEDIT_UNDOSTATECOUNT 99 285 | #endif 286 | #ifndef STB_TEXTEDIT_UNDOCHARCOUNT 287 | #define STB_TEXTEDIT_UNDOCHARCOUNT 999 288 | #endif 289 | #ifndef STB_TEXTEDIT_CHARTYPE 290 | #define STB_TEXTEDIT_CHARTYPE int 291 | #endif 292 | #ifndef STB_TEXTEDIT_POSITIONTYPE 293 | #define STB_TEXTEDIT_POSITIONTYPE int 294 | #endif 295 | 296 | typedef struct 297 | { 298 | // private data 299 | STB_TEXTEDIT_POSITIONTYPE where; 300 | short insert_length; 301 | short delete_length; 302 | short char_storage; 303 | } StbUndoRecord; 304 | 305 | typedef struct 306 | { 307 | // private data 308 | StbUndoRecord undo_rec[STB_TEXTEDIT_UNDOSTATECOUNT]; 309 | STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT]; 310 | short undo_point, redo_point; 311 | short undo_char_point, redo_char_point; 312 | } StbUndoState; 313 | 314 | typedef struct 315 | { 316 | ///////////////////// 317 | // 318 | // public data 319 | // 320 | 321 | int cursor; 322 | // position of the text cursor within the string 323 | 324 | int select_start; // selection start point 325 | int select_end; 326 | // selection start and end point in characters; if equal, no selection. 327 | // note that start may be less than or greater than end (e.g. when 328 | // dragging the mouse, start is where the initial click was, and you 329 | // can drag in either direction) 330 | 331 | unsigned char insert_mode; 332 | // each textfield keeps its own insert mode state. to keep an app-wide 333 | // insert mode, copy this value in/out of the app state 334 | 335 | ///////////////////// 336 | // 337 | // private data 338 | // 339 | unsigned char cursor_at_end_of_line; // not implemented yet 340 | unsigned char initialized; 341 | unsigned char has_preferred_x; 342 | unsigned char single_line; 343 | unsigned char padding1, padding2, padding3; 344 | float preferred_x; // this determines where the cursor up/down tries to seek to along x 345 | StbUndoState undostate; 346 | } STB_TexteditState; 347 | 348 | 349 | //////////////////////////////////////////////////////////////////////// 350 | // 351 | // StbTexteditRow 352 | // 353 | // Result of layout query, used by stb_textedit to determine where 354 | // the text in each row is. 355 | 356 | // result of layout query 357 | typedef struct 358 | { 359 | float x0, x1; // starting x location, end x location (allows for align=right, etc) 360 | float baseline_y_delta; // position of baseline relative to previous row's baseline 361 | float ymin, ymax; // height of row above and below baseline 362 | int num_chars; 363 | } StbTexteditRow; 364 | #endif //INCLUDE_STB_TEXTEDIT_H 365 | 366 | 367 | //////////////////////////////////////////////////////////////////////////// 368 | //////////////////////////////////////////////////////////////////////////// 369 | //// 370 | //// Implementation mode 371 | //// 372 | //// 373 | 374 | 375 | // implementation isn't include-guarded, since it might have indirectly 376 | // included just the "header" portion 377 | #ifdef STB_TEXTEDIT_IMPLEMENTATION 378 | 379 | #ifndef STB_TEXTEDIT_memmove 380 | #include 381 | #define STB_TEXTEDIT_memmove memmove 382 | #endif 383 | 384 | 385 | ///////////////////////////////////////////////////////////////////////////// 386 | // 387 | // Mouse input handling 388 | // 389 | 390 | // traverse the layout to locate the nearest character to a display position 391 | static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) 392 | { 393 | StbTexteditRow r; 394 | int n = STB_TEXTEDIT_STRINGLEN(str); 395 | float base_y = 0, prev_x; 396 | int i = 0, k; 397 | 398 | r.x0 = r.x1 = 0; 399 | r.ymin = r.ymax = 0; 400 | r.num_chars = 0; 401 | 402 | // search rows to find one that straddles 'y' 403 | while (i < n) { 404 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); 405 | if (r.num_chars <= 0) 406 | return n; 407 | 408 | if (i == 0 && y < base_y + r.ymin) 409 | return 0; 410 | 411 | if (y < base_y + r.ymax) 412 | break; 413 | 414 | i += r.num_chars; 415 | base_y += r.baseline_y_delta; 416 | } 417 | 418 | // below all text, return 'after' last character 419 | if (i >= n) 420 | return n; 421 | 422 | // check if it's before the beginning of the line 423 | if (x < r.x0) 424 | return i; 425 | 426 | // check if it's before the end of the line 427 | if (x < r.x1) { 428 | // search characters in row for one that straddles 'x' 429 | prev_x = r.x0; 430 | for (k = 0; k < r.num_chars; ++k) { 431 | float w = STB_TEXTEDIT_GETWIDTH(str, i, k); 432 | if (x < prev_x + w) { 433 | if (x < prev_x + w / 2) 434 | return k + i; 435 | else 436 | return k + i + 1; 437 | } 438 | prev_x += w; 439 | } 440 | // shouldn't happen, but if it does, fall through to end-of-line case 441 | } 442 | 443 | // if the last character is a newline, return that. otherwise return 'after' the last character 444 | if (STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) == STB_TEXTEDIT_NEWLINE) 445 | return i + r.num_chars - 1; 446 | else 447 | return i + r.num_chars; 448 | } 449 | 450 | // API click: on mouse down, move the cursor to the clicked location, and reset the selection 451 | static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 452 | { 453 | state->cursor = stb_text_locate_coord(str, x, y); 454 | state->select_start = state->cursor; 455 | state->select_end = state->cursor; 456 | state->has_preferred_x = 0; 457 | } 458 | 459 | // API drag: on mouse drag, move the cursor and selection endpoint to the clicked location 460 | static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 461 | { 462 | int p = stb_text_locate_coord(str, x, y); 463 | if (state->select_start == state->select_end) 464 | state->select_start = state->cursor; 465 | state->cursor = state->select_end = p; 466 | } 467 | 468 | ///////////////////////////////////////////////////////////////////////////// 469 | // 470 | // Keyboard input handling 471 | // 472 | 473 | // forward declarations 474 | static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); 475 | static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); 476 | static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); 477 | static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); 478 | static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); 479 | 480 | typedef struct 481 | { 482 | float x, y; // position of n'th character 483 | float height; // height of line 484 | int first_char, length; // first char of row, and length 485 | int prev_first; // first char of previous row 486 | } StbFindState; 487 | 488 | // find the x/y location of a character, and remember info about the previous row in 489 | // case we get a move-up event (for page up, we'll have to rescan) 490 | static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line) 491 | { 492 | StbTexteditRow r; 493 | int prev_start = 0; 494 | int z = STB_TEXTEDIT_STRINGLEN(str); 495 | int i = 0, first; 496 | 497 | if (n == z) { 498 | // if it's at the end, then find the last line -- simpler than trying to 499 | // explicitly handle this case in the regular code 500 | if (single_line) { 501 | STB_TEXTEDIT_LAYOUTROW(&r, str, 0); 502 | find->y = 0; 503 | find->first_char = 0; 504 | find->length = z; 505 | find->height = r.ymax - r.ymin; 506 | find->x = r.x1; 507 | } 508 | else { 509 | find->y = 0; 510 | find->x = 0; 511 | find->height = 1; 512 | while (i < z) { 513 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); 514 | prev_start = i; 515 | i += r.num_chars; 516 | } 517 | find->first_char = i; 518 | find->length = 0; 519 | find->prev_first = prev_start; 520 | } 521 | return; 522 | } 523 | 524 | // search rows to find the one that straddles character n 525 | find->y = 0; 526 | 527 | for (;;) { 528 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); 529 | if (n < i + r.num_chars) 530 | break; 531 | prev_start = i; 532 | i += r.num_chars; 533 | find->y += r.baseline_y_delta; 534 | } 535 | 536 | find->first_char = first = i; 537 | find->length = r.num_chars; 538 | find->height = r.ymax - r.ymin; 539 | find->prev_first = prev_start; 540 | 541 | // now scan to find xpos 542 | find->x = r.x0; 543 | i = 0; 544 | for (i = 0; first + i < n; ++i) 545 | find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); 546 | } 547 | 548 | #define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) 549 | 550 | // make the selection/cursor state valid if client altered the string 551 | static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 552 | { 553 | int n = STB_TEXTEDIT_STRINGLEN(str); 554 | if (STB_TEXT_HAS_SELECTION(state)) { 555 | if (state->select_start > n) state->select_start = n; 556 | if (state->select_end > n) state->select_end = n; 557 | // if clamping forced them to be equal, move the cursor to match 558 | if (state->select_start == state->select_end) 559 | state->cursor = state->select_start; 560 | } 561 | if (state->cursor > n) state->cursor = n; 562 | } 563 | 564 | // delete characters while updating undo 565 | static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) 566 | { 567 | stb_text_makeundo_delete(str, state, where, len); 568 | STB_TEXTEDIT_DELETECHARS(str, where, len); 569 | state->has_preferred_x = 0; 570 | } 571 | 572 | // delete the section 573 | static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 574 | { 575 | stb_textedit_clamp(str, state); 576 | if (STB_TEXT_HAS_SELECTION(state)) { 577 | if (state->select_start < state->select_end) { 578 | stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start); 579 | state->select_end = state->cursor = state->select_start; 580 | } 581 | else { 582 | stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end); 583 | state->select_start = state->cursor = state->select_end; 584 | } 585 | state->has_preferred_x = 0; 586 | } 587 | } 588 | 589 | // canoncialize the selection so start <= end 590 | static void stb_textedit_sortselection(STB_TexteditState *state) 591 | { 592 | if (state->select_end < state->select_start) { 593 | int temp = state->select_end; 594 | state->select_end = state->select_start; 595 | state->select_start = temp; 596 | } 597 | } 598 | 599 | // move cursor to first character of selection 600 | static void stb_textedit_move_to_first(STB_TexteditState *state) 601 | { 602 | if (STB_TEXT_HAS_SELECTION(state)) { 603 | stb_textedit_sortselection(state); 604 | state->cursor = state->select_start; 605 | state->select_end = state->select_start; 606 | state->has_preferred_x = 0; 607 | } 608 | } 609 | 610 | // move cursor to last character of selection 611 | static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 612 | { 613 | if (STB_TEXT_HAS_SELECTION(state)) { 614 | stb_textedit_sortselection(state); 615 | stb_textedit_clamp(str, state); 616 | state->cursor = state->select_end; 617 | state->select_start = state->select_end; 618 | state->has_preferred_x = 0; 619 | } 620 | } 621 | 622 | #ifdef STB_TEXTEDIT_IS_SPACE 623 | static int is_word_boundary(STB_TEXTEDIT_STRING *str, int idx) 624 | { 625 | return idx > 0 ? (STB_TEXTEDIT_IS_SPACE(STB_TEXTEDIT_GETCHAR(str, idx - 1)) && !STB_TEXTEDIT_IS_SPACE(STB_TEXTEDIT_GETCHAR(str, idx))) : 1; 626 | } 627 | 628 | #ifndef STB_TEXTEDIT_MOVEWORDLEFT 629 | static int stb_textedit_move_to_word_previous(STB_TEXTEDIT_STRING *str, int c) 630 | { 631 | --c; // always move at least one character 632 | while (c >= 0 && !is_word_boundary(str, c)) 633 | --c; 634 | 635 | if (c < 0) 636 | c = 0; 637 | 638 | return c; 639 | } 640 | #define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous 641 | #endif 642 | 643 | #ifndef STB_TEXTEDIT_MOVEWORDRIGHT 644 | static int stb_textedit_move_to_word_next(STB_TEXTEDIT_STRING *str, int c) 645 | { 646 | const int len = STB_TEXTEDIT_STRINGLEN(str); 647 | ++c; // always move at least one character 648 | while (c < len && !is_word_boundary(str, c)) 649 | ++c; 650 | 651 | if (c > len) 652 | c = len; 653 | 654 | return c; 655 | } 656 | #define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next 657 | #endif 658 | 659 | #endif 660 | 661 | // update selection and cursor to match each other 662 | static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) 663 | { 664 | if (!STB_TEXT_HAS_SELECTION(state)) 665 | state->select_start = state->select_end = state->cursor; 666 | else 667 | state->cursor = state->select_end; 668 | } 669 | 670 | // API cut: delete selection 671 | static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 672 | { 673 | if (STB_TEXT_HAS_SELECTION(state)) { 674 | stb_textedit_delete_selection(str, state); // implicity clamps 675 | state->has_preferred_x = 0; 676 | return 1; 677 | } 678 | return 0; 679 | } 680 | 681 | // API paste: replace existing selection with passed-in text 682 | static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) 683 | { 684 | STB_TEXTEDIT_CHARTYPE *text = (STB_TEXTEDIT_CHARTYPE *)ctext; 685 | // if there's a selection, the paste should delete it 686 | stb_textedit_clamp(str, state); 687 | stb_textedit_delete_selection(str, state); 688 | // try to insert the characters 689 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { 690 | stb_text_makeundo_insert(state, state->cursor, len); 691 | state->cursor += len; 692 | state->has_preferred_x = 0; 693 | return 1; 694 | } 695 | // remove the undo since we didn't actually insert the characters 696 | if (state->undostate.undo_point) 697 | --state->undostate.undo_point; 698 | return 0; 699 | } 700 | 701 | // API key: process a keyboard input 702 | static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) 703 | { 704 | retry: 705 | switch (key) { 706 | default: { 707 | int c = STB_TEXTEDIT_KEYTOTEXT(key); 708 | if (c > 0) { 709 | STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE)c; 710 | 711 | // can't add newline in single-line mode 712 | if (c == '\n' && state->single_line) 713 | break; 714 | 715 | if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { 716 | stb_text_makeundo_replace(str, state, state->cursor, 1, 1); 717 | STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); 718 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { 719 | ++state->cursor; 720 | state->has_preferred_x = 0; 721 | } 722 | } 723 | else { 724 | stb_textedit_delete_selection(str, state); // implicity clamps 725 | if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { 726 | stb_text_makeundo_insert(state, state->cursor, 1); 727 | ++state->cursor; 728 | state->has_preferred_x = 0; 729 | } 730 | } 731 | } 732 | break; 733 | } 734 | 735 | #ifdef STB_TEXTEDIT_K_INSERT 736 | case STB_TEXTEDIT_K_INSERT: 737 | state->insert_mode = !state->insert_mode; 738 | break; 739 | #endif 740 | 741 | case STB_TEXTEDIT_K_UNDO: 742 | stb_text_undo(str, state); 743 | state->has_preferred_x = 0; 744 | break; 745 | 746 | case STB_TEXTEDIT_K_REDO: 747 | stb_text_redo(str, state); 748 | state->has_preferred_x = 0; 749 | break; 750 | 751 | case STB_TEXTEDIT_K_LEFT: 752 | // if currently there's a selection, move cursor to start of selection 753 | if (STB_TEXT_HAS_SELECTION(state)) 754 | stb_textedit_move_to_first(state); 755 | else 756 | if (state->cursor > 0) 757 | --state->cursor; 758 | state->has_preferred_x = 0; 759 | break; 760 | 761 | case STB_TEXTEDIT_K_RIGHT: 762 | // if currently there's a selection, move cursor to end of selection 763 | if (STB_TEXT_HAS_SELECTION(state)) 764 | stb_textedit_move_to_last(str, state); 765 | else 766 | ++state->cursor; 767 | stb_textedit_clamp(str, state); 768 | state->has_preferred_x = 0; 769 | break; 770 | 771 | case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT: 772 | stb_textedit_clamp(str, state); 773 | stb_textedit_prep_selection_at_cursor(state); 774 | // move selection left 775 | if (state->select_end > 0) 776 | --state->select_end; 777 | state->cursor = state->select_end; 778 | state->has_preferred_x = 0; 779 | break; 780 | 781 | #ifdef STB_TEXTEDIT_MOVEWORDLEFT 782 | case STB_TEXTEDIT_K_WORDLEFT: 783 | if (STB_TEXT_HAS_SELECTION(state)) 784 | stb_textedit_move_to_first(state); 785 | else { 786 | state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); 787 | stb_textedit_clamp(str, state); 788 | } 789 | break; 790 | 791 | case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT: 792 | if (!STB_TEXT_HAS_SELECTION(state)) 793 | stb_textedit_prep_selection_at_cursor(state); 794 | 795 | state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); 796 | state->select_end = state->cursor; 797 | 798 | stb_textedit_clamp(str, state); 799 | break; 800 | #endif 801 | 802 | #ifdef STB_TEXTEDIT_MOVEWORDRIGHT 803 | case STB_TEXTEDIT_K_WORDRIGHT: 804 | if (STB_TEXT_HAS_SELECTION(state)) 805 | stb_textedit_move_to_last(str, state); 806 | else { 807 | state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); 808 | stb_textedit_clamp(str, state); 809 | } 810 | break; 811 | 812 | case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT: 813 | if (!STB_TEXT_HAS_SELECTION(state)) 814 | stb_textedit_prep_selection_at_cursor(state); 815 | 816 | state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); 817 | state->select_end = state->cursor; 818 | 819 | stb_textedit_clamp(str, state); 820 | break; 821 | #endif 822 | 823 | case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: 824 | stb_textedit_prep_selection_at_cursor(state); 825 | // move selection right 826 | ++state->select_end; 827 | stb_textedit_clamp(str, state); 828 | state->cursor = state->select_end; 829 | state->has_preferred_x = 0; 830 | break; 831 | 832 | case STB_TEXTEDIT_K_DOWN: 833 | case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: { 834 | StbFindState find; 835 | StbTexteditRow row; 836 | int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; 837 | 838 | if (state->single_line) { 839 | // on windows, up&down in single-line behave like left&right 840 | key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT); 841 | goto retry; 842 | } 843 | 844 | if (sel) 845 | stb_textedit_prep_selection_at_cursor(state); 846 | else if (STB_TEXT_HAS_SELECTION(state)) 847 | stb_textedit_move_to_last(str, state); 848 | 849 | // compute current position of cursor point 850 | stb_textedit_clamp(str, state); 851 | stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 852 | 853 | // now find character position down a row 854 | if (find.length) { 855 | float goal_x = state->has_preferred_x ? state->preferred_x : find.x; 856 | float x; 857 | int start = find.first_char + find.length; 858 | state->cursor = start; 859 | STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); 860 | x = row.x0; 861 | for (i = 0; i < row.num_chars; ++i) { 862 | float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); 863 | #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE 864 | if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) 865 | break; 866 | #endif 867 | x += dx; 868 | if (x > goal_x) 869 | break; 870 | ++state->cursor; 871 | } 872 | stb_textedit_clamp(str, state); 873 | 874 | state->has_preferred_x = 1; 875 | state->preferred_x = goal_x; 876 | 877 | if (sel) 878 | state->select_end = state->cursor; 879 | } 880 | break; 881 | } 882 | 883 | case STB_TEXTEDIT_K_UP: 884 | case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: { 885 | StbFindState find; 886 | StbTexteditRow row; 887 | int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; 888 | 889 | if (state->single_line) { 890 | // on windows, up&down become left&right 891 | key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT); 892 | goto retry; 893 | } 894 | 895 | if (sel) 896 | stb_textedit_prep_selection_at_cursor(state); 897 | else if (STB_TEXT_HAS_SELECTION(state)) 898 | stb_textedit_move_to_first(state); 899 | 900 | // compute current position of cursor point 901 | stb_textedit_clamp(str, state); 902 | stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 903 | 904 | // can only go up if there's a previous row 905 | if (find.prev_first != find.first_char) { 906 | // now find character position up a row 907 | float goal_x = state->has_preferred_x ? state->preferred_x : find.x; 908 | float x; 909 | state->cursor = find.prev_first; 910 | STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); 911 | x = row.x0; 912 | for (i = 0; i < row.num_chars; ++i) { 913 | float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); 914 | #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE 915 | if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) 916 | break; 917 | #endif 918 | x += dx; 919 | if (x > goal_x) 920 | break; 921 | ++state->cursor; 922 | } 923 | stb_textedit_clamp(str, state); 924 | 925 | state->has_preferred_x = 1; 926 | state->preferred_x = goal_x; 927 | 928 | if (sel) 929 | state->select_end = state->cursor; 930 | } 931 | break; 932 | } 933 | 934 | case STB_TEXTEDIT_K_DELETE: 935 | case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT: 936 | if (STB_TEXT_HAS_SELECTION(state)) 937 | stb_textedit_delete_selection(str, state); 938 | else { 939 | int n = STB_TEXTEDIT_STRINGLEN(str); 940 | if (state->cursor < n) 941 | stb_textedit_delete(str, state, state->cursor, 1); 942 | } 943 | state->has_preferred_x = 0; 944 | break; 945 | 946 | case STB_TEXTEDIT_K_BACKSPACE: 947 | case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT: 948 | if (STB_TEXT_HAS_SELECTION(state)) 949 | stb_textedit_delete_selection(str, state); 950 | else { 951 | stb_textedit_clamp(str, state); 952 | if (state->cursor > 0) { 953 | stb_textedit_delete(str, state, state->cursor - 1, 1); 954 | --state->cursor; 955 | } 956 | } 957 | state->has_preferred_x = 0; 958 | break; 959 | 960 | #ifdef STB_TEXTEDIT_K_TEXTSTART2 961 | case STB_TEXTEDIT_K_TEXTSTART2: 962 | #endif 963 | case STB_TEXTEDIT_K_TEXTSTART: 964 | state->cursor = state->select_start = state->select_end = 0; 965 | state->has_preferred_x = 0; 966 | break; 967 | 968 | #ifdef STB_TEXTEDIT_K_TEXTEND2 969 | case STB_TEXTEDIT_K_TEXTEND2: 970 | #endif 971 | case STB_TEXTEDIT_K_TEXTEND: 972 | state->cursor = STB_TEXTEDIT_STRINGLEN(str); 973 | state->select_start = state->select_end = 0; 974 | state->has_preferred_x = 0; 975 | break; 976 | 977 | #ifdef STB_TEXTEDIT_K_TEXTSTART2 978 | case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: 979 | #endif 980 | case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: 981 | stb_textedit_prep_selection_at_cursor(state); 982 | state->cursor = state->select_end = 0; 983 | state->has_preferred_x = 0; 984 | break; 985 | 986 | #ifdef STB_TEXTEDIT_K_TEXTEND2 987 | case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT: 988 | #endif 989 | case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: 990 | stb_textedit_prep_selection_at_cursor(state); 991 | state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); 992 | state->has_preferred_x = 0; 993 | break; 994 | 995 | 996 | #ifdef STB_TEXTEDIT_K_LINESTART2 997 | case STB_TEXTEDIT_K_LINESTART2: 998 | #endif 999 | case STB_TEXTEDIT_K_LINESTART: 1000 | stb_textedit_clamp(str, state); 1001 | stb_textedit_move_to_first(state); 1002 | if (state->single_line) 1003 | state->cursor = 0; 1004 | else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor - 1) != STB_TEXTEDIT_NEWLINE) 1005 | --state->cursor; 1006 | state->has_preferred_x = 0; 1007 | break; 1008 | 1009 | #ifdef STB_TEXTEDIT_K_LINEEND2 1010 | case STB_TEXTEDIT_K_LINEEND2: 1011 | #endif 1012 | case STB_TEXTEDIT_K_LINEEND: { 1013 | int n = STB_TEXTEDIT_STRINGLEN(str); 1014 | stb_textedit_clamp(str, state); 1015 | stb_textedit_move_to_first(state); 1016 | if (state->single_line) 1017 | state->cursor = n; 1018 | else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) 1019 | ++state->cursor; 1020 | state->has_preferred_x = 0; 1021 | break; 1022 | } 1023 | 1024 | #ifdef STB_TEXTEDIT_K_LINESTART2 1025 | case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT: 1026 | #endif 1027 | case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: 1028 | stb_textedit_clamp(str, state); 1029 | stb_textedit_prep_selection_at_cursor(state); 1030 | if (state->single_line) 1031 | state->cursor = 0; 1032 | else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor - 1) != STB_TEXTEDIT_NEWLINE) 1033 | --state->cursor; 1034 | state->select_end = state->cursor; 1035 | state->has_preferred_x = 0; 1036 | break; 1037 | 1038 | #ifdef STB_TEXTEDIT_K_LINEEND2 1039 | case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: 1040 | #endif 1041 | case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { 1042 | int n = STB_TEXTEDIT_STRINGLEN(str); 1043 | stb_textedit_clamp(str, state); 1044 | stb_textedit_prep_selection_at_cursor(state); 1045 | if (state->single_line) 1046 | state->cursor = n; 1047 | else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) 1048 | ++state->cursor; 1049 | state->select_end = state->cursor; 1050 | state->has_preferred_x = 0; 1051 | break; 1052 | } 1053 | 1054 | // @TODO: 1055 | // STB_TEXTEDIT_K_PGUP - move cursor up a page 1056 | // STB_TEXTEDIT_K_PGDOWN - move cursor down a page 1057 | } 1058 | } 1059 | 1060 | ///////////////////////////////////////////////////////////////////////////// 1061 | // 1062 | // Undo processing 1063 | // 1064 | // @OPTIMIZE: the undo/redo buffer should be circular 1065 | 1066 | static void stb_textedit_flush_redo(StbUndoState *state) 1067 | { 1068 | state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; 1069 | state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; 1070 | } 1071 | 1072 | // discard the oldest entry in the undo list 1073 | static void stb_textedit_discard_undo(StbUndoState *state) 1074 | { 1075 | if (state->undo_point > 0) { 1076 | // if the 0th undo state has characters, clean those up 1077 | if (state->undo_rec[0].char_storage >= 0) { 1078 | int n = state->undo_rec[0].insert_length, i; 1079 | // delete n characters from all other records 1080 | state->undo_char_point = state->undo_char_point - (short)n; // vsnet05 1081 | STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t)((size_t)state->undo_char_point * sizeof(STB_TEXTEDIT_CHARTYPE))); 1082 | for (i = 0; i < state->undo_point; ++i) 1083 | if (state->undo_rec[i].char_storage >= 0) 1084 | state->undo_rec[i].char_storage = state->undo_rec[i].char_storage - (short)n; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it 1085 | } 1086 | --state->undo_point; 1087 | STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec + 1, (size_t)((size_t)state->undo_point * sizeof(state->undo_rec[0]))); 1088 | } 1089 | } 1090 | 1091 | // discard the oldest entry in the redo list--it's bad if this 1092 | // ever happens, but because undo & redo have to store the actual 1093 | // characters in different cases, the redo character buffer can 1094 | // fill up even though the undo buffer didn't 1095 | static void stb_textedit_discard_redo(StbUndoState *state) 1096 | { 1097 | int k = STB_TEXTEDIT_UNDOSTATECOUNT - 1; 1098 | 1099 | if (state->redo_point <= k) { 1100 | // if the k'th undo state has characters, clean those up 1101 | if (state->undo_rec[k].char_storage >= 0) { 1102 | int n = state->undo_rec[k].insert_length, i; 1103 | // delete n characters from all other records 1104 | state->redo_char_point = state->redo_char_point + (short)n; // vsnet05 1105 | STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point - n, (size_t)((size_t)(STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point) * sizeof(STB_TEXTEDIT_CHARTYPE))); 1106 | for (i = state->redo_point; i < k; ++i) 1107 | if (state->undo_rec[i].char_storage >= 0) 1108 | state->undo_rec[i].char_storage = state->undo_rec[i].char_storage + (short)n; // vsnet05 1109 | } 1110 | STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point, state->undo_rec + state->redo_point - 1, (size_t)((size_t)(STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point) * sizeof(state->undo_rec[0]))); 1111 | ++state->redo_point; 1112 | } 1113 | } 1114 | 1115 | static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars) 1116 | { 1117 | // any time we create a new undo record, we discard redo 1118 | stb_textedit_flush_redo(state); 1119 | 1120 | // if we have no free records, we have to make room, by sliding the 1121 | // existing records down 1122 | if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1123 | stb_textedit_discard_undo(state); 1124 | 1125 | // if the characters to store won't possibly fit in the buffer, we can't undo 1126 | if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) { 1127 | state->undo_point = 0; 1128 | state->undo_char_point = 0; 1129 | return NULL; 1130 | } 1131 | 1132 | // if we don't have enough free characters in the buffer, we have to make room 1133 | while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT) 1134 | stb_textedit_discard_undo(state); 1135 | 1136 | return &state->undo_rec[state->undo_point++]; 1137 | } 1138 | 1139 | static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) 1140 | { 1141 | StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); 1142 | if (r == NULL) 1143 | return NULL; 1144 | 1145 | r->where = pos; 1146 | r->insert_length = (short)insert_len; 1147 | r->delete_length = (short)delete_len; 1148 | 1149 | if (insert_len == 0) { 1150 | r->char_storage = -1; 1151 | return NULL; 1152 | } 1153 | else { 1154 | r->char_storage = state->undo_char_point; 1155 | state->undo_char_point = state->undo_char_point + (short)insert_len; 1156 | return &state->undo_char[r->char_storage]; 1157 | } 1158 | } 1159 | 1160 | static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 1161 | { 1162 | StbUndoState *s = &state->undostate; 1163 | StbUndoRecord u, *r; 1164 | if (s->undo_point == 0) 1165 | return; 1166 | 1167 | // we need to do two things: apply the undo record, and create a redo record 1168 | u = s->undo_rec[s->undo_point - 1]; 1169 | r = &s->undo_rec[s->redo_point - 1]; 1170 | r->char_storage = -1; 1171 | 1172 | r->insert_length = u.delete_length; 1173 | r->delete_length = u.insert_length; 1174 | r->where = u.where; 1175 | 1176 | if (u.delete_length) { 1177 | // if the undo record says to delete characters, then the redo record will 1178 | // need to re-insert the characters that get deleted, so we need to store 1179 | // them. 1180 | 1181 | // there are three cases: 1182 | // there's enough room to store the characters 1183 | // characters stored for *redoing* don't leave room for redo 1184 | // characters stored for *undoing* don't leave room for redo 1185 | // if the last is true, we have to bail 1186 | 1187 | if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) { 1188 | // the undo records take up too much character space; there's no space to store the redo characters 1189 | r->insert_length = 0; 1190 | } 1191 | else { 1192 | int i; 1193 | 1194 | // there's definitely room to store the characters eventually 1195 | while (s->undo_char_point + u.delete_length > s->redo_char_point) { 1196 | // there's currently not enough room, so discard a redo record 1197 | stb_textedit_discard_redo(s); 1198 | // should never happen: 1199 | if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1200 | return; 1201 | } 1202 | r = &s->undo_rec[s->redo_point - 1]; 1203 | 1204 | r->char_storage = s->redo_char_point - u.delete_length; 1205 | s->redo_char_point = s->redo_char_point - (short)u.delete_length; 1206 | 1207 | // now save the characters 1208 | for (i = 0; i < u.delete_length; ++i) 1209 | s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i); 1210 | } 1211 | 1212 | // now we can carry out the deletion 1213 | STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length); 1214 | } 1215 | 1216 | // check type of recorded action: 1217 | if (u.insert_length) { 1218 | // easy case: was a deletion, so we need to insert n characters 1219 | STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); 1220 | s->undo_char_point -= u.insert_length; 1221 | } 1222 | 1223 | state->cursor = u.where + u.insert_length; 1224 | 1225 | s->undo_point--; 1226 | s->redo_point--; 1227 | } 1228 | 1229 | static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 1230 | { 1231 | StbUndoState *s = &state->undostate; 1232 | StbUndoRecord *u, r; 1233 | if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1234 | return; 1235 | 1236 | // we need to do two things: apply the redo record, and create an undo record 1237 | u = &s->undo_rec[s->undo_point]; 1238 | r = s->undo_rec[s->redo_point]; 1239 | 1240 | // we KNOW there must be room for the undo record, because the redo record 1241 | // was derived from an undo record 1242 | 1243 | u->delete_length = r.insert_length; 1244 | u->insert_length = r.delete_length; 1245 | u->where = r.where; 1246 | u->char_storage = -1; 1247 | 1248 | if (r.delete_length) { 1249 | // the redo record requires us to delete characters, so the undo record 1250 | // needs to store the characters 1251 | 1252 | if (s->undo_char_point + u->insert_length > s->redo_char_point) { 1253 | u->insert_length = 0; 1254 | u->delete_length = 0; 1255 | } 1256 | else { 1257 | int i; 1258 | u->char_storage = s->undo_char_point; 1259 | s->undo_char_point = s->undo_char_point + u->insert_length; 1260 | 1261 | // now save the characters 1262 | for (i = 0; i < u->insert_length; ++i) 1263 | s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i); 1264 | } 1265 | 1266 | STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length); 1267 | } 1268 | 1269 | if (r.insert_length) { 1270 | // easy case: need to insert n characters 1271 | STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); 1272 | s->redo_char_point += r.insert_length; 1273 | } 1274 | 1275 | state->cursor = r.where + r.insert_length; 1276 | 1277 | s->undo_point++; 1278 | s->redo_point++; 1279 | } 1280 | 1281 | static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length) 1282 | { 1283 | stb_text_createundo(&state->undostate, where, 0, length); 1284 | } 1285 | 1286 | static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) 1287 | { 1288 | int i; 1289 | STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); 1290 | if (p) { 1291 | for (i = 0; i < length; ++i) 1292 | p[i] = STB_TEXTEDIT_GETCHAR(str, where + i); 1293 | } 1294 | } 1295 | 1296 | static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) 1297 | { 1298 | int i; 1299 | STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); 1300 | if (p) { 1301 | for (i = 0; i < old_length; ++i) 1302 | p[i] = STB_TEXTEDIT_GETCHAR(str, where + i); 1303 | } 1304 | } 1305 | 1306 | // reset the state to default 1307 | static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line) 1308 | { 1309 | state->undostate.undo_point = 0; 1310 | state->undostate.undo_char_point = 0; 1311 | state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; 1312 | state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; 1313 | state->select_end = state->select_start = 0; 1314 | state->cursor = 0; 1315 | state->has_preferred_x = 0; 1316 | state->preferred_x = 0; 1317 | state->cursor_at_end_of_line = 0; 1318 | state->initialized = 1; 1319 | state->single_line = (unsigned char)is_single_line; 1320 | state->insert_mode = 0; 1321 | } 1322 | 1323 | // API initialize 1324 | static void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) 1325 | { 1326 | stb_textedit_clear_state(state, is_single_line); 1327 | } 1328 | #endif//STB_TEXTEDIT_IMPLEMENTATION 1329 | --------------------------------------------------------------------------------