├── 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 |
--------------------------------------------------------------------------------