├── .gitattributes
├── .gitignore
├── README.md
├── WaWMapExporter.sln
├── WaWMapExporter.vcxproj
├── WaWMapExporter.vcxproj.filters
├── cg
├── cg_hooks.cpp
├── cg_hooks.hpp
├── cg_init.cpp
├── cg_init.hpp
├── cg_local.hpp
└── cg_offsets.hpp
├── cm
├── cm_brush.cpp
├── cm_brush.hpp
├── cm_export.cpp
├── cm_export.hpp
├── cm_model.cpp
├── cm_model.hpp
├── cm_terrain.cpp
├── cm_terrain.hpp
├── cm_typedefs.cpp
└── cm_typedefs.hpp
├── cmd
├── cmd.cpp
└── cmd.hpp
├── com
├── com_vector.cpp
└── com_vector.hpp
├── dllmain.cpp
├── framework.h
├── fs
├── fs_globals.cpp
├── fs_globals.hpp
├── fs_io.cpp
└── fs_io.hpp
├── global_macros.hpp
├── mh
├── MinHook.h
├── buffer.c
├── buffer.h
├── hde
│ ├── hde32.c
│ ├── hde32.h
│ ├── hde64.c
│ ├── hde64.h
│ ├── pstdint.h
│ ├── table32.h
│ └── table64.h
├── hook.c
├── trampoline.c
└── trampoline.h
├── pch.cpp
├── pch.h
└── utils
├── engine.cpp
├── engine.hpp
├── errors.hpp
├── functions.cpp
├── functions.hpp
├── hook.hpp
├── resolution.cpp
├── resolution.hpp
└── typedefs.hpp
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Oo]ut/
33 | [Ll]og/
34 | [Ll]ogs/
35 |
36 | # Visual Studio 2015/2017 cache/options directory
37 | .vs/
38 | # Uncomment if you have tasks that create the project's static files in wwwroot
39 | #wwwroot/
40 |
41 | # Visual Studio 2017 auto generated files
42 | Generated\ Files/
43 |
44 | # MSTest test Results
45 | [Tt]est[Rr]esult*/
46 | [Bb]uild[Ll]og.*
47 |
48 | # NUnit
49 | *.VisualState.xml
50 | TestResult.xml
51 | nunit-*.xml
52 |
53 | # Build Results of an ATL Project
54 | [Dd]ebugPS/
55 | [Rr]eleasePS/
56 | dlldata.c
57 |
58 | # Benchmark Results
59 | BenchmarkDotNet.Artifacts/
60 |
61 | # .NET Core
62 | project.lock.json
63 | project.fragment.lock.json
64 | artifacts/
65 |
66 | # ASP.NET Scaffolding
67 | ScaffoldingReadMe.txt
68 |
69 | # StyleCop
70 | StyleCopReport.xml
71 |
72 | # Files built by Visual Studio
73 | *_i.c
74 | *_p.c
75 | *_h.h
76 | *.ilk
77 | *.meta
78 | *.obj
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio LightSwitch build output
298 | **/*.HTMLClient/GeneratedArtifacts
299 | **/*.DesktopClient/GeneratedArtifacts
300 | **/*.DesktopClient/ModelManifest.xml
301 | **/*.Server/GeneratedArtifacts
302 | **/*.Server/ModelManifest.xml
303 | _Pvt_Extensions
304 |
305 | # Paket dependency manager
306 | .paket/paket.exe
307 | paket-files/
308 |
309 | # FAKE - F# Make
310 | .fake/
311 |
312 | # CodeRush personal settings
313 | .cr/personal
314 |
315 | # Python Tools for Visual Studio (PTVS)
316 | __pycache__/
317 | *.pyc
318 |
319 | # Cake - Uncomment if you are using it
320 | # tools/**
321 | # !tools/packages.config
322 |
323 | # Tabs Studio
324 | *.tss
325 |
326 | # Telerik's JustMock configuration file
327 | *.jmconfig
328 |
329 | # BizTalk build output
330 | *.btp.cs
331 | *.btm.cs
332 | *.odx.cs
333 | *.xsd.cs
334 |
335 | # OpenCover UI analysis results
336 | OpenCover/
337 |
338 | # Azure Stream Analytics local run output
339 | ASALocalRun/
340 |
341 | # MSBuild Binary and Structured Log
342 | *.binlog
343 |
344 | # NVidia Nsight GPU debugger configuration file
345 | *.nvuser
346 |
347 | # MFractors (Xamarin productivity tool) working folder
348 | .mfractor/
349 |
350 | # Local History for Visual Studio
351 | .localhistory/
352 |
353 | # BeatPulse healthcheck temp database
354 | healthchecksdb
355 |
356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
357 | MigrationBackup/
358 |
359 | # Ionide (cross platform F# VS Code tools) working folder
360 | .ionide/
361 |
362 | # Fody - auto-generated XML schema
363 | FodyWeavers.xsd
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WaWMapExporter
2 |
3 | **ONLY** for the CoD WaW patch *1.7.1263*\
4 | Might have small geometry artifacts as this is almost copy paste from cod4 (and its .map format)
5 |
6 |
7 | ## How to use
8 | 1. Download the dll
9 | 2. Launch the game
10 | 3. Inject the dll
11 | 4. Launch any map
12 | 5. Write /cm_mapexport into the console
13 | 6. If everything was successful, there should be a /map_source/kej/ subdirectory in your game directory
14 |
15 | yea I wrote this in about 4-6 hours
16 |
--------------------------------------------------------------------------------
/WaWMapExporter.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.9.34902.65
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WaWMapExporter", "WaWMapExporter.vcxproj", "{7E3D4F1A-A008-4F20-BF0E-30DD2ED2C2EF}"
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 | {7E3D4F1A-A008-4F20-BF0E-30DD2ED2C2EF}.Debug|x64.ActiveCfg = Debug|x64
17 | {7E3D4F1A-A008-4F20-BF0E-30DD2ED2C2EF}.Debug|x64.Build.0 = Debug|x64
18 | {7E3D4F1A-A008-4F20-BF0E-30DD2ED2C2EF}.Debug|x86.ActiveCfg = Debug|Win32
19 | {7E3D4F1A-A008-4F20-BF0E-30DD2ED2C2EF}.Debug|x86.Build.0 = Debug|Win32
20 | {7E3D4F1A-A008-4F20-BF0E-30DD2ED2C2EF}.Release|x64.ActiveCfg = Release|x64
21 | {7E3D4F1A-A008-4F20-BF0E-30DD2ED2C2EF}.Release|x64.Build.0 = Release|x64
22 | {7E3D4F1A-A008-4F20-BF0E-30DD2ED2C2EF}.Release|x86.ActiveCfg = Release|Win32
23 | {7E3D4F1A-A008-4F20-BF0E-30DD2ED2C2EF}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {995952F8-6A03-47BA-A77D-5F63563DB896}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/WaWMapExporter.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 | 17.0
23 | Win32Proj
24 | {7e3d4f1a-a008-4f20-bf0e-30dd2ed2c2ef}
25 | WaWMapExporter
26 | 10.0
27 |
28 |
29 |
30 | DynamicLibrary
31 | true
32 | v143
33 | Unicode
34 |
35 |
36 | DynamicLibrary
37 | false
38 | v143
39 | true
40 | MultiByte
41 |
42 |
43 | DynamicLibrary
44 | true
45 | v143
46 | Unicode
47 |
48 |
49 | DynamicLibrary
50 | false
51 | v143
52 | true
53 | Unicode
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | Level3
76 | true
77 | WIN32;_DEBUG;WAWMAPEXPORTER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
78 | true
79 | Use
80 | pch.h
81 |
82 |
83 | Windows
84 | true
85 | false
86 |
87 |
88 |
89 |
90 | Level4
91 | true
92 | true
93 | true
94 | WIN32;NDEBUG;WAWMAPEXPORTER_EXPORTS;_WINDOWS;_USRDLL;NOMINMAX;%(PreprocessorDefinitions)
95 | true
96 | NotUsing
97 | pch.h
98 | stdcpplatest
99 | $(ProjectDir);$(DXSDK_DIR)Include
100 | true
101 |
102 |
103 | Windows
104 | true
105 | true
106 | true
107 | false
108 | D:\Activision\Call of Duty 5 - World at War\miles\map_exporter.dll
109 | $(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories)
110 | Winmm.lib;%(AdditionalDependencies)
111 |
112 |
113 |
114 |
115 | Level3
116 | true
117 | _DEBUG;WAWMAPEXPORTER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
118 | true
119 | Use
120 | pch.h
121 |
122 |
123 | Windows
124 | true
125 | false
126 |
127 |
128 |
129 |
130 | Level3
131 | true
132 | true
133 | true
134 | NDEBUG;WAWMAPEXPORTER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
135 | true
136 | Use
137 | pch.h
138 |
139 |
140 | Windows
141 | true
142 | true
143 | true
144 | false
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 | Create
191 | Create
192 | Create
193 | Create
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
--------------------------------------------------------------------------------
/WaWMapExporter.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;h++;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 | {b53cd5cb-ec73-402b-a988-39ea29950e82}
18 |
19 |
20 | {f4ef5c88-94c8-4d4c-86ef-0d4f99adc7db}
21 |
22 |
23 | {bc7c2ad4-e5fd-46ec-a276-ea354aceeb43}
24 |
25 |
26 | {c798bb06-51fd-46cb-930b-17e962ec806d}
27 |
28 |
29 | {3adc7521-b7d7-4059-9dda-aec53a1a1f25}
30 |
31 |
32 | {a0acc8e3-e849-44d3-9d33-df7618050504}
33 |
34 |
35 | {95e7e92b-ee50-4c2a-8a11-6cdd2aae4a10}
36 |
37 |
38 |
39 |
40 | Header Files
41 |
42 |
43 | Header Files
44 |
45 |
46 | Source Files\cg
47 |
48 |
49 | Source Files\cg
50 |
51 |
52 | Source Files\utils
53 |
54 |
55 | Source Files\utils
56 |
57 |
58 | Source Files\utils
59 |
60 |
61 | Source Files\utils
62 |
63 |
64 | Source Files\utils
65 |
66 |
67 | Source Files\utils
68 |
69 |
70 | Source Files\cm
71 |
72 |
73 | Source Files\cm
74 |
75 |
76 | Source Files\cm
77 |
78 |
79 | Header Files
80 |
81 |
82 | Source Files\cg
83 |
84 |
85 | Source Files\cg
86 |
87 |
88 | Source Files\com
89 |
90 |
91 | Source Files\fs
92 |
93 |
94 | Source Files\fs
95 |
96 |
97 | Source Files\cm
98 |
99 |
100 | Source Files\cm
101 |
102 |
103 | Source Files\cmd
104 |
105 |
106 |
107 |
108 | Source Files
109 |
110 |
111 | Source Files
112 |
113 |
114 | Source Files\cg
115 |
116 |
117 | Source Files\cg
118 |
119 |
120 | Source Files\utils
121 |
122 |
123 | Source Files\utils
124 |
125 |
126 | Source Files\utils
127 |
128 |
129 | Header Files\mh
130 |
131 |
132 | Header Files\mh
133 |
134 |
135 | Header Files\mh
136 |
137 |
138 | Header Files\mh
139 |
140 |
141 | Header Files\mh
142 |
143 |
144 | Source Files\cm
145 |
146 |
147 | Source Files\cm
148 |
149 |
150 | Source Files\cm
151 |
152 |
153 | Source Files\com
154 |
155 |
156 | Source Files\fs
157 |
158 |
159 | Source Files\fs
160 |
161 |
162 | Source Files\cm
163 |
164 |
165 | Source Files\cm
166 |
167 |
168 | Source Files\cmd
169 |
170 |
171 |
--------------------------------------------------------------------------------
/cg/cg_hooks.cpp:
--------------------------------------------------------------------------------
1 | #include "utils/hook.hpp"
2 | #include
3 |
4 | #include
5 | #include
6 | using namespace std::chrono_literals;
7 |
8 | static void CG_CreateHooks();
9 |
10 | void CG_CreatePermaHooks()
11 | {
12 | hooktable::initialize();
13 |
14 | CG_CreateHooks();
15 |
16 | }
17 | void CG_CreateHooks()
18 | {
19 | hooktable::preserver("__asm_adjacency_winding", 0x5D87FC, __brush::__asm_adjacency_winding);
20 |
21 |
22 | }
--------------------------------------------------------------------------------
/cg/cg_hooks.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 |
4 | void CG_CreatePermaHooks();
5 | void CG_ReleaseHooks();
6 |
7 |
--------------------------------------------------------------------------------
/cg/cg_init.cpp:
--------------------------------------------------------------------------------
1 | #include "cg/cg_hooks.hpp"
2 | #include "cg_init.hpp"
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include "cg/cg_local.hpp"
9 | #include "cg/cg_offsets.hpp"
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | #include "cmd/cmd.hpp"
16 |
17 | using namespace std::chrono_literals;
18 |
19 |
20 | void CG_Init()
21 | {
22 | std::this_thread::sleep_for(300ms);
23 | CG_CreatePermaHooks();
24 |
25 | Cmd_AddCommand("cm_mapexport", CM_MapExport);
26 |
27 | while (true) {
28 | std::this_thread::sleep_for(500ms);
29 |
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/cg/cg_init.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | void CG_Init();
4 | void CG_Cleanup();
5 |
--------------------------------------------------------------------------------
/cg/cg_local.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | struct cmd_function_s
4 | {
5 | cmd_function_s* next;
6 | const char* name;
7 | const char* autoCompleteDir;
8 | const char* autoCompleteExt;
9 | void(__cdecl* function)();
10 | };
11 |
12 |
13 | struct XModelLodInfo
14 | {
15 | float dist;
16 | unsigned __int16 numsurfs;
17 | unsigned __int16 surfIndex;
18 | int partBits[4];
19 | char lod;
20 | char smcIndexPlusOne;
21 | char smcAllocBits;
22 | char unused;
23 | };
24 | struct XModelHighMipBounds
25 | {
26 | float mins[3];
27 | float maxs[3];
28 | };
29 |
30 | struct XModelStreamInfo
31 | {
32 | XModelHighMipBounds* highMipBounds;
33 | };
34 | struct XModel
35 | {
36 | const char* name;
37 | char numBones;
38 | char numRootBones;
39 | unsigned char numsurfs;
40 | char lodRampType;
41 | unsigned __int16* boneNames;
42 | char* parentList;
43 | __int16* quats;
44 | float* trans;
45 | char* partClassification;
46 | struct DObjAnimMat* baseMat;
47 | struct XSurface* surfs;
48 | struct Material** materialHandles;
49 | XModelLodInfo lodInfo[4];
50 | struct XModelCollSurf_s* collSurfs;
51 | int numCollSurfs;
52 | int contents;
53 | struct XBoneInfo* boneInfo;
54 | float radius;
55 | float mins[3];
56 | float maxs[3];
57 | __int16 numLods;
58 | __int16 collLod;
59 | XModelStreamInfo streamInfo;
60 | int memUsage;
61 | char flags;
62 | bool bad;
63 | struct PhysPreset* physPreset;
64 | struct PhysGeomList* physGeoms;
65 | };
66 |
67 | struct GfxPackedPlacement
68 | {
69 | float origin[3];
70 | float axis[3][3];
71 | float scale;
72 | };
73 |
74 | struct __declspec(align(4)) GfxStaticModelDrawInst
75 | {
76 | float cullDist;
77 | GfxPackedPlacement placement;
78 | XModel* model;
79 | unsigned __int16 smodelCacheIndex[4];
80 | char reflectionProbeIndex;
81 | char primaryLightIndex;
82 | unsigned __int16 lightingHandle;
83 | char flags;
84 | char pad[18];
85 | };
86 |
87 | struct SimplePlaneIntersection
88 | {
89 | float xyz[3];
90 | int planeIndex[3];
91 | };
92 | struct adjacencyWinding_t
93 | {
94 | int numsides;
95 | int sides[12];
96 | };
97 |
98 | struct cplane_s
99 | {
100 | float normal[3];
101 | float dist;
102 | char type; // for fast side tests: 0,1,2 = axial, 3 = nonaxial
103 | char signbits; // signx + (signy<<1) + (signz<<2), used as lookup during collision
104 | char pad[2];
105 | };
106 |
107 | #pragma pack(push, 2)
108 | struct cbrushside_t
109 | {
110 | cplane_s* plane;
111 | unsigned int materialNum;
112 | __int16 firstAdjacentSideOffset;
113 | char edgeCount;
114 | };
115 | #pragma pack(pop)
116 |
117 | #pragma pack(push, 16)
118 | struct cbrush_t
119 | {
120 | float mins[3];
121 | int contents;
122 | float maxs[3];
123 | unsigned int numsides;
124 | cbrushside_t* sides;
125 | __int16 axialMaterialNum[2][3];
126 | char* baseAdjacentSide;
127 | __int16 firstAdjacentSideOffsets[2][3];
128 | char edgeCount[2][3];
129 | __int16 colorCounter;
130 | __int16 cmBrushIndex;
131 | __int16 cmSubmodelIndex;
132 | bool isSubmodel;
133 | bool pad;
134 | };
135 | #pragma pack(pop)
136 |
137 | struct dmaterial_t
138 | {
139 | char material[64];
140 | int surfaceFlags;
141 | int contentFlags;
142 | };
143 | struct CollisionBorder
144 | {
145 | float distEq[3];
146 | float zBase;
147 | float zSlope;
148 | float start;
149 | float length;
150 | };
151 |
152 | struct CollisionPartition
153 | {
154 | char triCount;
155 | char borderCount;
156 | int firstTri;
157 | int hmm;
158 | int hmm2;
159 | CollisionBorder* borders;
160 | };
161 |
162 |
163 | union CollisionAabbTreeIndex
164 | {
165 | int firstChildIndex;
166 | int partitionIndex;
167 | };
168 |
169 | struct CollisionAabbTree
170 | {
171 | float origin[3];
172 | unsigned __int16 materialIndex;
173 | unsigned __int16 childCount;
174 | float halfSize[3];
175 | CollisionAabbTreeIndex u;
176 | };
177 |
178 | #pragma pack(push, 4)
179 | struct cLeaf_t
180 | {
181 | unsigned __int16 firstCollAabbIndex;
182 | unsigned __int16 collAabbCount;
183 | int brushContents;
184 | int terrainContents;
185 | float mins[3];
186 | float maxs[3];
187 | int leafBrushNode;
188 | __int16 cluster;
189 | };
190 | #pragma pack(pop)
191 | struct clipMap_t
192 | {
193 | char* name;
194 | int isInUse;
195 | int planeCount;
196 | void* cplane_s___planes;
197 | unsigned int numStaticModels;
198 | struct cStaticModel_s* staticModelList;
199 | unsigned int numMaterials;
200 | dmaterial_t* materials;
201 | unsigned int numBrushSides;
202 | void* cbrushside_t___brushsides;
203 | unsigned int numBrushEdges;
204 | unsigned __int8* brushEdges;
205 | unsigned int numNodes;
206 | void* cNode_t___nodes;
207 | unsigned int numLeafs;
208 | cLeaf_t* leafs;
209 | unsigned int leafbrushNodesCount;
210 | void* cLeafBrushNode_s___leafbrushNodes;
211 | unsigned int numLeafBrushes;
212 | unsigned __int16* leafbrushes;
213 | unsigned int numLeafSurfaces;
214 | unsigned int* leafsurfaces;
215 | unsigned int vertCount;
216 | float(*verts)[3];
217 | unsigned int numBrushVerts;
218 | float(*brushVerts)[3];
219 | unsigned int nuinds;
220 | unsigned __int16* uinds;
221 | int triCount;
222 | unsigned __int16* triIndices;
223 | unsigned __int8* triEdgeIsWalkable;
224 | int borderCount;
225 | void* CollisionBorder___borders;
226 | int partitionCount;
227 | CollisionPartition* partitions;
228 | int aabbTreeCount;
229 | CollisionAabbTree* aabbTrees;
230 | unsigned int numSubModels;
231 | void* cmodel_t___cmodels;
232 | unsigned __int16 numBrushes;
233 | cbrush_t* brushes;
234 | int numClusters;
235 | int clusterBytes;
236 | unsigned __int8* visibility;
237 | int vised;
238 | struct MapEnts* mapEnts;
239 | void* cbrush_t___box_brush;
240 | };
241 |
242 |
--------------------------------------------------------------------------------
/cg/cg_offsets.hpp:
--------------------------------------------------------------------------------
1 |
2 | struct clipMap_t;
3 | struct cmd_function_s;
4 |
5 | inline clipMap_t* cm = reinterpret_cast(0x2223A80);
6 | inline cmd_function_s** cmd_functions = reinterpret_cast(0x222377C);
7 |
--------------------------------------------------------------------------------
/cm/cm_brush.cpp:
--------------------------------------------------------------------------------
1 | #include "cm_brush.hpp"
2 | #include "cm_typedefs.hpp"
3 | #include "cg/cg_local.hpp"
4 | #include
5 | #include
6 | #include
7 |
8 | SimplePlaneIntersection pts[1024];
9 | SimplePlaneIntersection* pts_results[1024];
10 |
11 | void CM_GetBrushWindings(cbrush_t* brush)
12 | {
13 | if (!brush)
14 | return;
15 |
16 | CClipMap::wip_geom = CM_GetBrushPoints(brush, { 0.f, 1.f, 0.f });
17 | CClipMap::insert(CClipMap::wip_geom);
18 |
19 | }
20 | std::unique_ptr CM_GetBrushPoints(cbrush_t* brush, const fvec3& poly_col)
21 | {
22 | if (!brush)
23 | return nullptr;
24 |
25 | float outPlanes[128][4]{};
26 | int planeCount = BrushToPlanes(brush, outPlanes);
27 | int intersections = GetPlaneIntersections((const float**)outPlanes, planeCount, pts);
28 | adjacencyWinding_t windings[128]{};
29 |
30 | int intersection = 0;
31 | int num_verts = 0;
32 |
33 | CClipMap::wip_geom = std::make_unique();
34 | CClipMap::wip_color = poly_col;
35 |
36 | auto c_brush = dynamic_cast(CClipMap::wip_geom.get());
37 |
38 | c_brush->brush = brush;
39 | c_brush->origin = fvec3(brush->mins) + ((fvec3(brush->maxs) - fvec3(brush->mins)) / 2);
40 |
41 | do {
42 | auto w = BuildBrushAdjacencyWindingForSide(intersections, (char*)"lol", outPlanes[intersection], intersection, pts, &windings[intersection]);
43 | if (w) {
44 | //std::cout << w->numsides << '\n';
45 | num_verts += w->numsides;
46 | }
47 | ++intersection;
48 | } while (intersection < planeCount);
49 |
50 | c_brush->num_verts = num_verts;
51 | c_brush->create_corners();
52 |
53 | return std::move(CClipMap::wip_geom);
54 |
55 | }
56 | void CM_BuildAxialPlanes(float(*planes)[6][4], const cbrush_t* brush)
57 | {
58 |
59 | (*planes)[0][0] = -1.0;
60 | (*planes)[0][1] = 0.0;
61 | (*planes)[0][2] = 0.0;
62 | (*planes)[0][3] = -brush->mins[0];
63 | (*planes)[1][0] = 1.0;
64 | (*planes)[1][1] = 0.0;
65 | (*planes)[1][2] = 0.0;
66 | (*planes)[1][3] = brush->maxs[0];
67 | (*planes)[2][0] = 0.0;
68 | (*planes)[2][2] = 0.0;
69 | (*planes)[2][1] = -1.0;
70 | (*planes)[2][3] = -brush->mins[1];
71 | (*planes)[3][0] = 0.0;
72 | (*planes)[3][2] = 0.0;
73 | (*planes)[3][1] = 1.0;
74 | (*planes)[3][3] = brush->maxs[1];
75 | (*planes)[4][0] = 0.0;
76 | (*planes)[4][1] = 0.0;
77 | (*planes)[4][2] = -1.0;
78 | (*planes)[4][3] = -brush->mins[2];
79 | (*planes)[5][0] = 0.0;
80 | (*planes)[5][1] = 0.0;
81 | (*planes)[5][2] = 1.0;
82 | (*planes)[5][3] = brush->maxs[2];
83 | }
84 | void CM_GetPlaneVec4Form(const cbrushside_t* sides, const float(*axialPlanes)[4], int index, float* expandedPlane)
85 | {
86 | if (index >= 6) {
87 | cplane_s* plane = sides[index - 6].plane;
88 |
89 | expandedPlane[0] = plane->normal[0];
90 | expandedPlane[1] = plane->normal[1];
91 | expandedPlane[2] = plane->normal[2];
92 | expandedPlane[3] = plane->dist;
93 | return;
94 | }
95 |
96 | const float* plane = axialPlanes[index];
97 |
98 | *expandedPlane = plane[0];
99 | expandedPlane[1] = plane[1];
100 | expandedPlane[2] = plane[2];
101 | expandedPlane[3] = plane[3];
102 |
103 | }
104 | int GetPlaneIntersections(const float** planes, int planeCount, SimplePlaneIntersection* OutPts)
105 | {
106 | int r = 0;
107 | __asm
108 | {
109 | push OutPts;
110 | push planeCount;
111 | push planes;
112 | mov esi, 0x5EBDB0;
113 | call esi;
114 | add esp, 12;
115 | mov r, eax;
116 | }
117 |
118 | return r;
119 | }
120 | int BrushToPlanes(const cbrush_t* brush, float(*outPlanes)[4])
121 | {
122 | float planes[6][4]{};
123 | CM_BuildAxialPlanes((float(*)[6][4])planes, brush);
124 | uint32_t i = 0;
125 | do {
126 | CM_GetPlaneVec4Form(brush->sides, planes, i, outPlanes[i]);
127 |
128 | } while (++i < brush->numsides + 6);
129 |
130 | return i;
131 | }
132 | adjacencyWinding_t* BuildBrushAdjacencyWindingForSide(int ptCount, char* collMap, float* normals, int planeIndex, SimplePlaneIntersection* spi, adjacencyWinding_t* optionalOutWinding)
133 | {
134 | adjacencyWinding_t* r = 0;
135 |
136 | __asm
137 | {
138 | mov ecx, ptCount;
139 | push optionalOutWinding;
140 | push spi;
141 | push planeIndex;
142 | push normals;
143 | push collMap;
144 | mov esi, 0x5D8430;
145 | call esi;
146 | add esp, 20;
147 | mov r, eax;
148 | }
149 |
150 | return r;
151 | }
152 | static void __cdecl adjacency_winding(adjacencyWinding_t* w, float* points, vec3_t normal, unsigned int i0, unsigned int i1, unsigned int i2)
153 | {
154 | auto brush = dynamic_cast(CClipMap::wip_geom.get());
155 | cm_triangle tri;
156 | std::vector winding_points;
157 |
158 | tri.a = &points[i2];
159 | tri.b = &points[i1];
160 | tri.c = &points[i0];
161 |
162 | PlaneFromPointsASM(tri.plane, tri.a, tri.b, tri.c);
163 |
164 | if (DotProduct(tri.plane, normal) < 0.f) {
165 | std::swap(tri.a, tri.c);
166 | }
167 |
168 | tri.material = CM_MaterialForNormal(brush->brush, normal);
169 | //tri.material = (char*)"caulk";
170 | brush->triangles.push_back(tri);
171 |
172 | for (int winding = 0; winding < w->numsides; winding++) {
173 | winding_points.push_back({ &points[winding * 3] });
174 | }
175 |
176 | brush->windings.push_back(cm_winding{ winding_points, normal, CClipMap::wip_color });
177 |
178 | }
179 | __declspec(naked) void __brush::__asm_adjacency_winding()
180 | {
181 | static constexpr unsigned int dst = 0x5D8812;
182 |
183 | __asm
184 | {
185 | mov eax, [esp + 6038h + -6014h]; //i2
186 | lea edx, [eax + eax * 2];
187 | mov eax, [esp + 6038h + -6020h]; //i1
188 | lea ecx, [eax + eax * 2];
189 | mov eax, [esp + 6038h + -6018h]; //i0
190 | lea esi, [eax + eax * 2];
191 |
192 | push edx; //i2
193 | push ecx; //i1
194 | push esi; //i0
195 | push ebp; //normal
196 |
197 | lea eax, [esp + 6048h - 3000h];
198 | push eax; //points
199 | push ebx; //winding
200 |
201 | call adjacency_winding;
202 | add esp, 24;
203 |
204 | movss xmm0, dword ptr[ebp + 04h];
205 | mulss xmm0, dword ptr[esp + 6038h + -600Ch];
206 | movss xmm1, dword ptr[ebp + 8h];
207 | mulss xmm1, dword ptr[esp + 6038h + -6008h];
208 |
209 | jmp dst;
210 | }
211 |
212 | }
213 | char* CM_MaterialForNormal(const cbrush_t* target, const fvec3& normals)
214 | {
215 | //non-axial!
216 | for (unsigned int i = 0; i < target->numsides; i++) {
217 |
218 | cbrushside_t* side = &target->sides[i];
219 |
220 | if (normals == side->plane->normal)
221 | return cm->materials[side->materialNum].material;
222 | }
223 |
224 |
225 | short mtl = -1;
226 |
227 | if (normals.z == 1.f)
228 | mtl = target->axialMaterialNum[1][2];
229 | else if (normals.z == -1.f)
230 | mtl = target->axialMaterialNum[0][2];
231 |
232 | if (normals.x == 1)
233 | mtl = target->axialMaterialNum[1][0];
234 | else if (normals.x == -1)
235 | mtl = target->axialMaterialNum[0][0];
236 |
237 | if (normals.y == 1.f)
238 | mtl = target->axialMaterialNum[1][1];
239 | else if (normals.y == -1.f)
240 | mtl = target->axialMaterialNum[0][1];
241 |
242 | if (mtl >= 0)
243 | return cm->materials[mtl].material;
244 |
245 | return nullptr;
246 |
247 | }
--------------------------------------------------------------------------------
/cm/cm_brush.hpp:
--------------------------------------------------------------------------------
1 |
2 | #include
3 |
4 | struct cm_geometry;
5 | struct cbrush_t;
6 | struct SimplePlaneIntersection;
7 | struct adjacencyWinding_t;
8 | struct cbrushside_t;
9 |
10 | void CM_GetBrushWindings(cbrush_t* brush);
11 | std::unique_ptr CM_GetBrushPoints(cbrush_t* brush, const fvec3& poly_col);
12 |
13 | void CM_BuildAxialPlanes(float(*planes)[6][4], const cbrush_t* brush);
14 | void CM_GetPlaneVec4Form(const cbrushside_t* sides, const float(*axialPlanes)[4], int index, float* expandedPlane);
15 | int GetPlaneIntersections(const float** planes, int planeCount, SimplePlaneIntersection* OutPts);
16 | int BrushToPlanes(const cbrush_t* brush, float(*outPlanes)[4]);
17 | adjacencyWinding_t* BuildBrushAdjacencyWindingForSide(int ptCount, char* collMap, float* normals, int planeIndex, SimplePlaneIntersection* pts, adjacencyWinding_t* optionalOutWinding);
18 | char* CM_MaterialForNormal(const cbrush_t* target, const fvec3& normals);
19 |
20 | namespace __brush
21 | {
22 | inline bool rb_requesting_to_stop_rendering = false; //a silly way to handle multithreaded rendering contexts
23 | void __asm_adjacency_winding();
24 | }
--------------------------------------------------------------------------------
/cm/cm_export.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "cm_brush.hpp"
3 | #include "cm_typedefs.hpp"
4 | #include "utils/errors.hpp"
5 | #include
6 | #include
7 |
8 | #include "cg/cg_local.hpp"
9 | #include "cg/cg_offsets.hpp"
10 | #include
11 |
12 | static void CM_WriteHeader(std::stringstream& f)
13 | {
14 | f << "iwmap 4\n";
15 | f << "\"000_Global\" flags active\n";
16 | f << "\"The Map\" flags\n";
17 | f << "// entity 0\n{\n";
18 | f << "\"contrastGain\" " "\"0.125\"" << '\n';
19 | f << "\"diffusefraction\" " "\"0.5\"" << '\n';
20 | f << "\"_color\" " "\"0.2 0.27 0.3 1\"" << '\n';
21 | f << "\"sunlight\" " "\"1\"" << '\n';
22 | f << "\"sundirection\" " "\"-30 -95 0\"" << '\n';
23 | f << "\"sundiffusecolor\" " "\"0.2 0.27 0.3 1\"" << '\n';
24 | f << "\"suncolor\" " "\"0.2 0.27 0.3 1\"" << '\n';
25 | f << "\"ambient\" " "\".1\"" << '\n';
26 | f << "\"bouncefraction\" " "\".7\"" << '\n';
27 | f << "\"classname\" \"worldspawn\"\n";
28 | }
29 | static void CM_WriteAllBrushes(std::stringstream& o)
30 | {
31 | if (CClipMap::size() == 0) {
32 | return FatalError("CClipMap::size() == 0");
33 | }
34 |
35 | int brushIndex = 0;
36 |
37 | auto& geo = CClipMap::get();
38 | bool entity_start = false;
39 |
40 | for (auto& geom : geo) {
41 |
42 |
43 |
44 | if (geom->type() == cm_geomtype::model && !entity_start) {
45 | entity_start = true;
46 | brushIndex = 1;
47 | //end brushes
48 | o << "}\n";
49 | }
50 |
51 | brushIndex = geom->map_export(o, brushIndex);
52 | }
53 |
54 | if (!entity_start) {
55 | o << "}\n";
56 | }
57 |
58 | }
59 |
60 | void CM_WriteInOrder(std::stringstream& o)
61 | {
62 | CM_WriteHeader(o);
63 | CM_WriteAllBrushes(o);
64 |
65 |
66 |
67 | }
68 |
69 | void CM_MapExport()
70 | {
71 | if (!cm->name)
72 | return;
73 |
74 | CM_LoadMap();
75 |
76 | std::string name = cm->name;
77 | name = name.substr(0u, name.length() - sizeof(".d3dbsp") + 1u);
78 | name = name.substr(sizeof("maps/mp"));
79 |
80 | const std::string path = "map_source\\kej\\";
81 | const std::string full_path = path + name + ".map";
82 |
83 | const auto writer = WaWIOWriter(full_path, false);
84 | std::stringstream map;
85 |
86 | CM_WriteInOrder(map);
87 |
88 | if (writer.IO_Write(map.str())) {
89 | const std::string str = std::format("^1{} export courtesy of the great\n^2xkejj^1.\n", name);
90 | ((void(*)(int, char*))0x562490)(5, (char*)str.c_str());
91 | }
92 | else {
93 | ((void(*)(int, char*))0x562490)(5, (char*)"^1Failed!\n");
94 | }
95 |
96 |
97 | }
--------------------------------------------------------------------------------
/cm/cm_export.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | void CM_MapExport();
4 |
--------------------------------------------------------------------------------
/cm/cm_model.cpp:
--------------------------------------------------------------------------------
1 | #include "cm_model.hpp"
2 | #include "cm_typedefs.hpp"
3 | #include "cg/cg_local.hpp"
4 | #include "cg/cg_offsets.hpp"
5 | #include
6 | #include
7 |
8 | __declspec(naked) GfxStaticModelDrawInst* GetStaticModelDrawInstPointer()
9 | {
10 |
11 | __asm
12 | {
13 | push ebx;
14 |
15 | mov eax, ds:1087DB2Ch;
16 | mov ebx, [eax + 2B0h];
17 | mov eax, ebx;
18 |
19 | pop ebx;
20 | retn;
21 | }
22 | }
23 |
24 | void CM_AddModel(const GfxStaticModelDrawInst* model)
25 | {
26 |
27 | cm_model xmodel;
28 |
29 | xmodel.modelscale = model->placement.scale;
30 | xmodel.origin = model->placement.origin;
31 | xmodel.angles = AxisToAngles(model->placement.axis);
32 | xmodel.name = model->model->name;
33 |
34 | CClipMap::insert(std::make_unique(xmodel));
35 | }
36 |
37 |
38 | int cm_model::map_export(std::stringstream& o, int index)
39 | {
40 |
41 | o << "// entity " << index << '\n';
42 | o << "{\n";
43 | o << std::quoted("angles") << ' ' << std::quoted(std::format("{} {} {}", angles.x, angles.y, angles.z)) << '\n';
44 | o << std::quoted("modelscale") << ' ' << std::quoted(std::format("{}", modelscale)) << '\n';
45 | o << std::quoted("origin") << ' ' << std::quoted(std::format("{} {} {}", origin.x, origin.y, origin.z)) << '\n';
46 | o << std::quoted("model") << ' ' << std::quoted(name) << '\n';
47 | o << std::quoted("classname") << ' ' << std::quoted("misc_model") << '\n';
48 | o << "}\n";
49 |
50 | return ++index;
51 |
52 | }
--------------------------------------------------------------------------------
/cm/cm_model.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | struct GfxStaticModelDrawInst;
4 | GfxStaticModelDrawInst* GetStaticModelDrawInstPointer();
5 |
6 | void CM_AddModel(const GfxStaticModelDrawInst* model);
--------------------------------------------------------------------------------
/cm/cm_terrain.cpp:
--------------------------------------------------------------------------------
1 | #include "cm_terrain.hpp"
2 | #include "cm_typedefs.hpp"
3 |
4 | #include "cg/cg_local.hpp"
5 | #include "cg/cg_offsets.hpp"
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #define MASK_PLAYERSOLID 0x02810011
12 |
13 | bool CM_AabbTreeHasCollision(const CollisionAabbTree* tree)
14 | {
15 | const dmaterial_t* materialInfo = &cm->materials[tree->materialIndex];
16 | return (materialInfo->contentFlags & MASK_PLAYERSOLID) != 0;
17 | }
18 |
19 | std::unordered_map discovered_partitions;
20 |
21 |
22 | bool CM_DiscoverTerrain([[maybe_unused]]const std::unordered_set& filters)
23 | {
24 |
25 | try {
26 | discovered_partitions.clear();
27 | for (const auto i : std::views::iota(0u, cm->numLeafs)) {
28 |
29 | auto terrain = CM_LeafToGeometry(&cm->leafs[i], filters);
30 |
31 | if (terrain)
32 | CClipMap::insert(terrain);
33 | }
34 | return true;
35 | }
36 | catch (...) {
37 | return false;
38 | }
39 | }
40 |
41 | void CM_AdvanceAabbTree(const CollisionAabbTree* aabbTree, cm_terrain* terrain, const std::unordered_set& filters, const float* color)
42 | {
43 | if (aabbTree->childCount) {
44 | auto child = &cm->aabbTrees[aabbTree->u.firstChildIndex];
45 | for ([[maybe_unused]]const auto i : std::views::iota(0u, aabbTree->childCount)) {
46 | CM_AdvanceAabbTree(child, terrain, filters, color);
47 | ++child;
48 | }
49 | return;
50 | }
51 |
52 | const auto mat = cm->materials[aabbTree->materialIndex].material;
53 | if (CM_IsMatchingFilter(filters, mat) == false) {
54 | return;
55 | }
56 |
57 | terrain->material = mat;
58 | terrain->color[0] = color[0];
59 | terrain->color[1] = color[1];
60 | terrain->color[2] = color[2];
61 | terrain->color[3] = color[3];
62 |
63 | CollisionAabbTreeIndex fChild = aabbTree->u;
64 | CollisionPartition* partition = &cm->partitions[fChild.partitionIndex];
65 |
66 | if (discovered_partitions.find(partition) != discovered_partitions.end())
67 | return;
68 |
69 | discovered_partitions[partition] = partition;
70 |
71 | auto firstTri = partition->firstTri;
72 | if (firstTri < firstTri + partition->triCount)
73 | {
74 |
75 | auto triIndice = 3 * firstTri;
76 |
77 | do {
78 | cm_triangle tri;
79 | tri.has_collision = CM_AabbTreeHasCollision(aabbTree);
80 | tri.a = cm->verts[cm->triIndices[triIndice]];
81 | tri.b = cm->verts[cm->triIndices[triIndice + 1]];
82 | tri.c = cm->verts[cm->triIndices[triIndice + 2]];
83 |
84 | PlaneFromPointsASM(tri.plane, tri.a, tri.b, tri.c);
85 |
86 | tri.color[0] = color[0];
87 | tri.color[1] = color[1];
88 | tri.color[2] = color[2];
89 | tri.color[3] = 0.3f;
90 |
91 | terrain->tris.emplace_back(tri);
92 |
93 | ++firstTri;
94 | triIndice += 3;
95 |
96 | } while (firstTri < partition->firstTri + partition->triCount);
97 |
98 | }
99 | }
100 |
101 | std::unique_ptr CM_LeafToGeometry(const cLeaf_t* leaf, const std::unordered_set& filters)
102 | {
103 | if (!leaf || !leaf->collAabbCount)
104 | return 0;
105 |
106 | std::int32_t aabbIdx = 0;
107 | cm_terrain terrain;
108 |
109 | terrain.leaf = leaf;
110 |
111 | do {
112 | const CollisionAabbTree* aabb = &cm->aabbTrees[aabbIdx + leaf->firstCollAabbIndex];
113 | CM_AdvanceAabbTree(aabb, &terrain, filters, vec4_t{ 0,0.1f,1.f, 0.8f });
114 | ++aabbIdx;
115 | } while (aabbIdx < leaf->collAabbCount);
116 |
117 |
118 | return std::make_unique(terrain);
119 | }
120 |
--------------------------------------------------------------------------------
/cm/cm_terrain.hpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | struct CollisionAabbTree;
6 | struct cLeaf_t;
7 |
8 | struct cm_terrain;
9 | struct cm_geometry;
10 |
11 | class CClipMap;
12 |
13 |
14 | bool CM_AabbTreeHasCollision(const CollisionAabbTree* tree);
15 | bool CM_DiscoverTerrain(const std::unordered_set& filters);
16 | void CM_AdvanceAabbTree(const CollisionAabbTree* aabbTree, cm_terrain* terrain, const std::unordered_set& filters, const float* color);
17 | std::unique_ptr CM_LeafToGeometry(const cLeaf_t* leaf, const std::unordered_set& filters);
18 |
--------------------------------------------------------------------------------
/cm/cm_typedefs.cpp:
--------------------------------------------------------------------------------
1 | #include "cg/cg_offsets.hpp"
2 | #include "cg/cg_local.hpp"
3 | #include "cm_brush.hpp"
4 | #include "cm_model.hpp"
5 | #include "cm_terrain.hpp"
6 | #include "cm_typedefs.hpp"
7 | #include
8 |
9 | #include
10 | #include
11 |
12 | geom_ptr CClipMap::geometry;
13 | std::unique_ptr CClipMap::wip_geom;
14 | fvec3 CClipMap::wip_color;
15 |
16 |
17 | cm_winding::cm_winding(const std::vector& p, const fvec3& normal, [[maybe_unused]]const fvec3& col) : points(p), normals(normal)
18 | {
19 | is_bounce = normal[2] >= 0.3f && normal[2] <= 0.7f;
20 | is_elevator = std::fabs(normal[0]) == 1.f || std::fabs(normal[1]) == 1.f;
21 | normals = normal;
22 |
23 | //if (rgp && rgp->world) {
24 | // fvec3 new_color = SetSurfaceBrightness(col, normal, rgp->world->sunParse.angles);
25 | // VectorCopy(new_color, color);
26 | //}
27 | color[1] = 1.f;
28 | color[2] = 0.f;
29 | color[3] = 1.f;
30 | color[3] = 0.7f;
31 |
32 | mins = get_mins();
33 | maxs = get_maxs();
34 | }
35 |
36 |
37 | void cm_brush::render([[maybe_unused]] const cm_renderinfo& info)
38 | {
39 | /*if (info.only_colliding && brush->has_collision() == false)
40 | return;
41 |
42 | if (origin.dist(cgs->predictedPlayerState.origin) > info.draw_dist)
43 | return;
44 |
45 | if (!CM_BrushInView(brush, info.frustum_planes, info.num_planes))
46 | return;
47 |
48 | for (auto& w : windings)
49 | {
50 |
51 |
52 | if (info.only_bounces && w.is_bounce == false)
53 | continue;
54 |
55 | if (info.only_elevators && w.is_elevator == false)
56 | continue;
57 |
58 | vec4_t c = { 0,1,1,0.3f };
59 |
60 | c[0] = w.color[0];
61 | c[1] = w.color[1];
62 | c[2] = w.color[2];
63 | c[3] = info.alpha;
64 |
65 | if (info.only_bounces) {
66 | float n = w.normals[2];
67 |
68 | if (n > 0.7f || n < 0.3f)
69 | n = 0.f;
70 | else
71 | n = 1.f - (n - 0.3f) / (0.7f - 0.3f);
72 |
73 | c[0] = 1.f - n;
74 | c[1] = n;
75 | c[2] = 0.f;
76 | }
77 |
78 | if (__brush::rb_requesting_to_stop_rendering)
79 | return;
80 |
81 | auto func = info.as_polygons ? RB_DrawCollisionPoly : RB_DrawCollisionEdges;
82 | func(w.points.size(), (float(*)[3])w.points.data(), c, info.depth_test);
83 |
84 | }
85 |
86 | if (info.only_elevators == 2 && brush->has_collision()) {
87 |
88 | std::vector pts(2);
89 |
90 | auto func = info.as_polygons ? RB_DrawCollisionPoly : RB_DrawCollisionEdges;
91 |
92 |
93 | for (auto& w : corners) {
94 | func(w->points.size(), (float(*)[3])w->points.data(), vec4_t{1,0,0,info.alpha}, info.depth_test);
95 | }
96 |
97 | }*/
98 |
99 | };
100 | void cm_brush::create_corners()
101 | {
102 | //get all ele surfaces
103 | std::vector ele_windings;
104 |
105 | std::for_each(windings.begin(), windings.end(), [&ele_windings](const cm_winding& w)
106 | {
107 | const bool is_elevator = std::fabs(w.normals[0]) == 1.f || std::fabs(w.normals[1]) == 1.f;
108 | if (w.normals[2] == 0.f && !is_elevator)
109 | ele_windings.push_back(&w);
110 | });
111 |
112 | corners = ele_windings;
113 |
114 |
115 |
116 | }
117 | int cm_brush::map_export(std::stringstream& o, int index)
118 | {
119 |
120 | std::vector new_points = triangles;
121 |
122 |
123 | o << "// brush " << index << '\n';
124 | o << "{\n";
125 |
126 | for (auto& tri : new_points)
127 | {
128 |
129 | o << std::format(" ( {} {} {} )", tri.a.x, tri.a.y, tri.a.z);
130 | o << std::format(" ( {} {} {} )", tri.b.x, tri.b.y, tri.b.z);
131 | o << std::format(" ( {} {} {} )", tri.c.x, tri.c.y, tri.c.z);
132 |
133 | std::string material = tri.material == nullptr ? "caulk" : tri.material;
134 |
135 | o << " " << material;
136 | o << " 128 128 0 0 0 0 lightmap_gray 16384 16384 0 0 0 0\n";
137 |
138 | }
139 |
140 | o << "}\n";
141 |
142 | return ++index;
143 | }
144 |
145 | void cm_terrain::render(const cm_renderinfo& info)
146 | {
147 | if (info.only_elevators)
148 | return;
149 | //if (children.size()) {
150 |
151 | // for (auto& child : children)
152 | // child.render(frustum);
153 |
154 | // return;
155 | //}
156 |
157 | /*std::vector points(3);
158 | fvec3 center;
159 |
160 | for (auto& tri : tris)
161 | {
162 | if (tri.has_collision == false && info.only_colliding)
163 | continue;
164 |
165 |
166 |
167 | if ((tri.plane[2] < 0.3f || tri.plane[2] > 0.7f) && info.only_bounces)
168 | continue;
169 |
170 | if (tri.a.dist(cgs->predictedPlayerState.origin) > info.draw_dist)
171 | continue;
172 |
173 | if (!CM_TriangleInView(&tri, info.frustum_planes, info.num_planes))
174 | continue;
175 |
176 | vec4_t c =
177 | {
178 | tri.color[0],
179 | tri.color[1],
180 | tri.color[2],
181 | info.alpha
182 | };
183 |
184 | if (info.only_bounces) {
185 | float n = tri.plane[2];
186 |
187 | if (n > 0.7f || n < 0.3f)
188 | n = 0.f;
189 | else
190 | n = 1.f - (n - 0.3f) / (0.7f - 0.3f);
191 |
192 | c[0] = 1.f - n;
193 | c[1] = n;
194 | c[2] = 0.f;
195 | }
196 |
197 |
198 | points[0] = (tri.a);
199 | points[1] = (tri.b);
200 | points[2] = (tri.c);
201 |
202 | center.x = { (points[0].x + points[1].x + points[2].x) / 3 };
203 | center.y = { (points[0].y + points[1].y + points[2].y) / 3 };
204 | center.z = { (points[0].z + points[1].z + points[2].z) / 3 };
205 |
206 | if (center.dist(cgs->predictedPlayerState.origin) > info.draw_dist)
207 | continue;
208 |
209 | if (info.as_polygons)
210 | RB_DrawPolyInteriors(3, points, c, true, info.depth_test);
211 | else
212 | RB_DrawCollisionEdges(3, (float(*)[3])points.data(), c, info.depth_test);
213 | }*/
214 | }
215 | int cm_terrain::map_export(std::stringstream& o, int index)
216 | {
217 | for (auto& tri : tris)
218 | index = map_export_triangle(o, tri, index);
219 |
220 | return index;
221 |
222 | }
223 | int cm_terrain::map_export_triangle(std::stringstream& o, const cm_triangle& tri, int index) const
224 | {
225 |
226 | o << "// brush " << index << '\n';
227 | o << " {\n";
228 | o << " mesh\n";
229 | o << " {\n";
230 | o << " " << material << '\n';
231 | o << " lightmap_gray\n";
232 | o << " smoothing smoothing_hard\n";
233 | o << " 2 2 16 8\n";
234 | o << " (\n";
235 | o << " v " << std::format("{} {} {} t -5760 5824 -46 54\n", tri.a.x, tri.a.y, tri.a.z);
236 | o << " v " << std::format("{} {} {} t -5760 5824 -46 54\n", tri.b.x, tri.b.y, tri.b.z);
237 | o << " )\n";
238 | o << " (\n";
239 | o << " v " << std::format("{} {} {} t -5760 5824 -46 54\n", tri.c.x, tri.c.y, tri.c.z);
240 | o << " v " << std::format("{} {} {} t -5760 5824 -46 54\n", tri.c.x, tri.c.y, tri.c.z);
241 | o << " )\n";
242 | o << " }\n";
243 | o << " }\n";
244 |
245 | return ++index;
246 | }
247 |
248 | void cm_terrain::render2d()
249 | {
250 |
251 | //size_t index = 0;
252 | //for (auto& tri : tris)
253 | //{
254 | // auto center = (tri.a + tri.b + tri.c) / 3;
255 |
256 | // if (center.dist(cgs->predictedPlayerState.origin) > 1000) {
257 | // index++;
258 | // continue;
259 | // }
260 |
261 | // if (const auto pos_opt = WorldToScreen(center)) {
262 | // auto& v = pos_opt.value();
263 | // std::string str = std::to_string(index);
264 | // R_DrawTextWithEffects(str, "fonts/bigDevFont", float(v.x), float(v.y), 1.f, 1.f, 0.f, vec4_t{ 1,1,1,1 }, 3, vec4_t{ 1,0,0,1 });
265 |
266 | // }
267 |
268 | // index++;
269 |
270 | //}
271 | }
272 | bool CM_IsMatchingFilter(const std::unordered_set& filters, const char* material)
273 | {
274 |
275 | for (const auto& filter : filters) {
276 |
277 | if (filter == "all" || std::string(material).contains(filter))
278 | return true;
279 | }
280 |
281 | return false;
282 | }
283 |
284 |
285 | void CM_LoadMap()
286 | {
287 |
288 | CClipMap::clear();
289 |
290 | for(const auto i : std::views::iota(0u, cm->numBrushes))
291 | CM_GetBrushWindings(&cm->brushes[i]);
292 |
293 | CM_DiscoverTerrain({ "all" });
294 |
295 | const auto smodelCount = *(uint32_t*)0x10F39EBC;
296 | const auto smodel = GetStaticModelDrawInstPointer();
297 |
298 | for (const auto i : std::views::iota(0u, smodelCount)) {
299 | CM_AddModel(&smodel[i]);
300 | }
301 |
302 |
303 | }
--------------------------------------------------------------------------------
/cm/cm_typedefs.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include "global_macros.hpp"
10 |
11 | struct sc_winding_t
12 | {
13 | std::vector points;
14 | bool is_bounce = false;
15 | bool is_elevator = false;
16 | fvec3 normals;
17 | vec4_t color;
18 |
19 | };
20 | enum class showCollisionType
21 | {
22 | DISABLED,
23 | BRUSHES,
24 | TERRAIN,
25 | BOTH
26 | };
27 | enum class polyType
28 | {
29 | EDGES,
30 | POLYS,
31 | };
32 |
33 | enum class cm_geomtype
34 | {
35 | brush,
36 | terrain,
37 | model
38 | };
39 |
40 | struct cplane_s;
41 | struct cbrush_t;
42 | struct cLeaf_t;
43 |
44 | struct cm_triangle
45 | {
46 | fvec3 a;
47 | fvec3 b;
48 | fvec3 c;
49 |
50 |
51 |
52 | vec4_t plane = {};
53 | vec4_t color = {};
54 | char* material = {};
55 | bool has_collision = false;
56 | //bool edge_walkable = true;
57 |
58 | fvec3 get_mins() const noexcept {
59 | fvec3 lowest = FLT_MAX;
60 |
61 | lowest.x = a.x;
62 | if (b.x < lowest.x) lowest.x = b.x;
63 | if (c.x < lowest.x) lowest.x = c.x;
64 |
65 | lowest.y = a.y;
66 | if (b.y < lowest.y) lowest.y = b.y;
67 | if (c.y < lowest.y) lowest.y = c.y;
68 |
69 | lowest.z = a.z;
70 | if (b.z < lowest.z) lowest.z = b.z;
71 | if (c.z < lowest.z) lowest.z = c.z;
72 |
73 | return lowest;
74 |
75 | }
76 | fvec3 get_maxs() const noexcept {
77 | fvec3 highest = -FLT_MAX;
78 |
79 | highest.x = a.x;
80 | if (b.x > highest.x) highest.x = b.x;
81 | if (c.x > highest.x) highest.x = c.x;
82 |
83 | highest.y = a.y;
84 | if (b.y > highest.y) highest.y = b.y;
85 | if (c.y > highest.y) highest.y = c.y;
86 |
87 | highest.z = a.z;
88 | if (b.z > highest.z) highest.z = b.z;
89 | if (c.z > highest.z) highest.z = c.z;
90 |
91 | return highest;
92 |
93 | }
94 | };
95 | struct cm_winding
96 | {
97 | cm_winding() = default;
98 | cm_winding(const std::vector& p, const fvec3& normal, const fvec3& col);
99 |
100 | std::vector points;
101 | fvec3 mins;
102 | fvec3 maxs;
103 | fvec3 normals;
104 | vec4_t color;
105 | bool is_bounce = {};
106 | bool is_elevator = {};
107 |
108 | private:
109 |
110 | inline fvec3 get_mins() const noexcept
111 | {
112 | std::vector x, y, z;
113 |
114 | for (auto& p : points) {
115 | x.push_back(p.x);
116 | y.push_back(p.y);
117 | z.push_back(p.z);
118 |
119 | }
120 |
121 | const float _x = *std::min_element(x.begin(), x.end());
122 | const float _y = *std::min_element(y.begin(), y.end());
123 | const float _z = *std::min_element(z.begin(), z.end());
124 |
125 | return { _x, _y, _z };
126 | }
127 | inline fvec3 get_maxs() const noexcept
128 | {
129 | std::vector x, y, z;
130 |
131 | for (auto& p : points) {
132 | x.push_back(p.x);
133 | y.push_back(p.y);
134 | z.push_back(p.z);
135 |
136 | }
137 |
138 | const float _x = *std::max_element(x.begin(), x.end());
139 | const float _y = *std::max_element(y.begin(), y.end());
140 | const float _z = *std::max_element(z.begin(), z.end());
141 |
142 | return { _x, _y, _z };
143 | }
144 | };
145 |
146 | struct cm_renderinfo
147 | {
148 | cplane_s* frustum_planes = {};
149 | int num_planes = {};
150 | float draw_dist = {};
151 | bool depth_test = {};
152 | bool as_polygons = {};
153 | bool only_colliding = {};
154 | bool only_bounces = {};
155 | int only_elevators = {};
156 | float alpha = 0.7f;
157 |
158 |
159 | };
160 |
161 | class brushModelEntity;
162 |
163 | struct cm_geometry
164 | {
165 | virtual ~cm_geometry() = default;
166 | virtual void render(const cm_renderinfo& info) = 0;
167 | virtual cm_geomtype type() const noexcept = 0;
168 | virtual int map_export(std::stringstream& o, int index) = 0;
169 | virtual void render2d() = 0;
170 | fvec3 origin;
171 | bool has_collisions = {};
172 | int num_verts = {};
173 | brushModelEntity* brushmodel = 0;
174 |
175 | };
176 |
177 |
178 | struct cm_brush : public cm_geometry
179 | {
180 | ~cm_brush() = default;
181 |
182 | std::vector windings; //used for rendering
183 | std::vector triangles; //used for exporting
184 |
185 | struct ele_corner {
186 | fvec3 mins;
187 | fvec3 maxs;
188 | };
189 |
190 | std::vector corners;
191 |
192 | cbrush_t* brush = {};
193 |
194 | void create_corners();
195 | void render(const cm_renderinfo& info) override;
196 | cm_geomtype type() const noexcept override { return cm_geomtype::brush; }
197 | void render2d() override {}
198 | protected:
199 | int map_export(std::stringstream& o, int index) override;
200 | };
201 |
202 | struct cm_terrain : public cm_geometry
203 | {
204 | ~cm_terrain() = default;
205 |
206 | void render(const cm_renderinfo& info) override;
207 | void render2d() override;
208 |
209 | //void sort_tree();
210 |
211 | const cLeaf_t* leaf = 0;
212 | std::vector tris;
213 | vec4_t color = {};
214 | char* material = {};
215 |
216 | cm_geomtype type() const noexcept override { return cm_geomtype::terrain; }
217 |
218 | protected:
219 | int map_export(std::stringstream& o, int index) override;
220 | int map_export_triangle(std::stringstream& o, const cm_triangle& tri, int index) const;
221 |
222 | private:
223 | };
224 |
225 | struct cm_model : public cm_geometry
226 | {
227 | ~cm_model() = default;
228 |
229 | void render([[maybe_unused]] const cm_renderinfo& info) override {};
230 | void render2d() override {};
231 |
232 | int map_export(std::stringstream& o, int index) override;
233 |
234 |
235 | const char* name = {};
236 | fvec3 origin;
237 | fvec3 angles;
238 | float modelscale = {};
239 |
240 |
241 | cm_geomtype type() const noexcept override { return cm_geomtype::model; }
242 |
243 | };
244 |
245 | void CM_LoadMap();
246 | bool CM_IsMatchingFilter(const std::unordered_set& filters, const char* material);
247 |
248 |
249 | using geom_ptr = std::vector>;
250 | class CClipMap
251 | {
252 | public:
253 |
254 | static void insert(std::unique_ptr& geom) {
255 |
256 | if (geom)
257 | geometry.emplace_back(std::move(geom));
258 |
259 | wip_geom = nullptr;
260 | }
261 | static void insert(std::unique_ptr&& geom) {
262 |
263 | if (geom)
264 | geometry.emplace_back(std::move(geom));
265 |
266 | wip_geom = nullptr;
267 | }
268 | static void clear_type(const cm_geomtype t)
269 | {
270 | auto itr = std::remove_if(geometry.begin(), geometry.end(), [&t](std::unique_ptr& g)
271 | {
272 | return g->type() == t;
273 | });
274 |
275 | geometry.erase(itr, geometry.end());
276 |
277 | }
278 | static std::vector get_all_of_type(const cm_geomtype t)
279 | {
280 | std::vector r;
281 |
282 | for (auto b = geometry.begin(); b != geometry.end(); ++b)
283 | {
284 | if (b->get()->type() == t)
285 | r.push_back(b);
286 | }
287 |
288 | return r;
289 | }
290 | static auto begin() { return geometry.begin(); }
291 | static auto end() { return geometry.end(); }
292 | static size_t size() { return geometry.size(); }
293 | static void clear() { geometry.clear(); wip_geom.reset(); }
294 | static auto& get() { return geometry; }
295 |
296 | static std::unique_ptr wip_geom;
297 | static fvec3 wip_color;
298 |
299 | private:
300 | static geom_ptr geometry;
301 | };
302 |
303 | void CM_LoadMap();
304 |
305 |
--------------------------------------------------------------------------------
/cmd/cmd.cpp:
--------------------------------------------------------------------------------
1 | #include "cg/cg_local.hpp"
2 | #include "cg/cg_offsets.hpp"
3 | #include "cmd.hpp"
4 | #include
5 | #include
6 | cmd_function_s cmds[24];
7 | std::uint32_t cmd_iterator = {};
8 | cmd_function_s* Cmd_FindCommand(const char* cmd)
9 | {
10 | cmd_function_s* result = 0;
11 | const std::uint32_t f = 0x55CC70;
12 | __asm
13 | {
14 | mov esi, cmd;
15 | call f;
16 | mov result, eax;
17 | }
18 |
19 | return result;
20 | }
21 | void Cmd_AddCommand(const char* name, void(*func)())
22 | {
23 | if (cmd_iterator >= 24 || Cmd_FindCommand(name)) {
24 | return;
25 | }
26 |
27 | auto& cmd = cmds[cmd_iterator++];
28 |
29 | cmd.function = func;
30 | cmd.name = name;
31 | cmd.next = *cmd_functions;
32 | *cmd_functions = &cmd;
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/cmd/cmd.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 |
4 | struct cmd_function_s;
5 |
6 | cmd_function_s* Cmd_FindCommand(const char* cmd);
7 | void Cmd_AddCommand(const char* name, void(*func)());
8 |
--------------------------------------------------------------------------------
/com/com_vector.cpp:
--------------------------------------------------------------------------------
1 | #include "com_vector.hpp"
2 | #include
3 |
4 | void CrossProduct(const vec3_t v1, const vec3_t v2, vec3_t cross) {
5 | cross[0] = v1[1] * v2[2] - v1[2] * v2[1];
6 | cross[1] = v1[2] * v2[0] - v1[0] * v2[2];
7 | cross[2] = v1[0] * v2[1] - v1[1] * v2[0];
8 | }
9 |
10 | void VectorInverse(vec3_t v) {
11 | v[0] = -v[0];
12 | v[1] = -v[1];
13 | v[2] = -v[2];
14 | }
15 |
16 | vec_t VectorNormalize(vec3_t v) {
17 | float length, ilength;
18 |
19 | length = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
20 | length = sqrt(length);
21 |
22 | if (length) {
23 | ilength = 1 / length;
24 | v[0] *= ilength;
25 | v[1] *= ilength;
26 | v[2] *= ilength;
27 | }
28 |
29 | return length;
30 | }
31 | vec_t VectorNormalize2(const vec3_t v, vec3_t out) {
32 | float length, ilength;
33 |
34 | length = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
35 | length = sqrt(length);
36 |
37 | if (length) {
38 | ilength = 1 / length;
39 | out[0] = v[0] * ilength;
40 | out[1] = v[1] * ilength;
41 | out[2] = v[2] * ilength;
42 | }
43 | else {
44 | VectorClear(out);
45 | }
46 |
47 | return length;
48 |
49 | }
50 |
51 | void PlaneFromPointsASM(float* plane, float* v0, float* v1, float* v2)
52 | {
53 | static constexpr unsigned int fnc = 0x5B8770;
54 | __asm
55 | {
56 | mov edi, v0;
57 | mov esi, plane;
58 | mov edx, v1;
59 | mov ecx, v2;
60 | call fnc;
61 | }
62 | }
63 | void MatrixMultiply(float in1[3][3], float in2[3][3], float out[3][3]) {
64 | out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0];
65 | out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1];
66 | out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2];
67 | out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0];
68 | out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1];
69 | out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2];
70 | out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0];
71 | out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1];
72 | out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2];
73 | }
74 | void ProjectPointOnPlane(vec3_t dst, const vec3_t p, const vec3_t normal) {
75 | float d{};
76 | vec3_t n{};
77 | float inv_denom = {};
78 |
79 | inv_denom = 1.0F / DotProduct(normal, normal);
80 |
81 | d = DotProduct(normal, p) * inv_denom;
82 |
83 | n[0] = normal[0] * inv_denom;
84 | n[1] = normal[1] * inv_denom;
85 | n[2] = normal[2] * inv_denom;
86 |
87 | dst[0] = p[0] - d * n[0];
88 | dst[1] = p[1] - d * n[1];
89 | dst[2] = p[2] - d * n[2];
90 | }
91 | void PerpendicularVector(vec3_t dst, const vec3_t src) {
92 | int pos = {};
93 | int i = {};
94 | float minelem = 1.0F;
95 | vec3_t tempvec = {};
96 |
97 | /*
98 | ** find the smallest magnitude axially aligned vector
99 | */
100 | for (pos = 0, i = 0; i < 3; i++)
101 | {
102 | if (fabs(src[i]) < minelem) {
103 | pos = i;
104 | minelem = fabs(src[i]);
105 | }
106 | }
107 | tempvec[0] = tempvec[1] = tempvec[2] = 0.0F;
108 | tempvec[pos] = 1.0F;
109 |
110 | /*
111 | ** project the point onto the plane defined by src
112 | */
113 | ProjectPointOnPlane(dst, tempvec, src);
114 |
115 | /*
116 | ** normalize the result
117 | */
118 | VectorNormalize(dst);
119 | }
120 |
121 | void RotatePointAroundVector(vec3_t dst, const vec3_t dir, const vec3_t point,
122 | float degrees) {
123 | float m[3][3] = {};
124 | float im[3][3] = {};
125 | float zrot[3][3] = {};
126 | float tmpmat[3][3] = {};
127 | float rot[3][3] = {};
128 | int i = {};
129 | vec3_t vr = {}, vup = {}, vf = {};
130 | float rad = {};
131 |
132 | vf[0] = dir[0];
133 | vf[1] = dir[1];
134 | vf[2] = dir[2];
135 |
136 | PerpendicularVector(vr, dir);
137 | CrossProduct(vr, vf, vup);
138 |
139 | m[0][0] = vr[0];
140 | m[1][0] = vr[1];
141 | m[2][0] = vr[2];
142 |
143 | m[0][1] = vup[0];
144 | m[1][1] = vup[1];
145 | m[2][1] = vup[2];
146 |
147 | m[0][2] = vf[0];
148 | m[1][2] = vf[1];
149 | m[2][2] = vf[2];
150 |
151 | memcpy(im, m, sizeof(im));
152 |
153 | im[0][1] = m[1][0];
154 | im[0][2] = m[2][0];
155 | im[1][0] = m[0][1];
156 | im[1][2] = m[2][1];
157 | im[2][0] = m[0][2];
158 | im[2][1] = m[1][2];
159 |
160 | memset(zrot, 0, sizeof(zrot));
161 | zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F;
162 |
163 | rad = DEG2RAD(degrees);
164 | zrot[0][0] = cos(rad);
165 | zrot[0][1] = sin(rad);
166 | zrot[1][0] = -sin(rad);
167 | zrot[1][1] = cos(rad);
168 |
169 | MatrixMultiply(m, zrot, tmpmat);
170 | MatrixMultiply(tmpmat, im, rot);
171 |
172 | for (i = 0; i < 3; i++) {
173 | dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2];
174 | }
175 | }
176 | void vectoangles(const vec3_t value1, vec3_t angles) {
177 | float forward;
178 | float yaw, pitch;
179 |
180 | if (value1[1] == 0 && value1[0] == 0) {
181 | yaw = 0;
182 | if (value1[2] > 0) {
183 | pitch = 90;
184 | }
185 | else {
186 | pitch = 270;
187 | }
188 | }
189 | else {
190 | if (value1[0]) {
191 | yaw = (atan2(value1[1], value1[0]) * 180 / M_PI);
192 | }
193 | else if (value1[1] > 0) {
194 | yaw = 90;
195 | }
196 | else {
197 | yaw = 270;
198 | }
199 | if (yaw < 0) {
200 | yaw += 360;
201 | }
202 | forward = sqrt(value1[0] * value1[0] + value1[1] * value1[1]);
203 | pitch = (atan2(value1[2], forward) * 180 / M_PI);
204 | if (pitch < 0) {
205 | pitch += 360;
206 | }
207 | }
208 |
209 | angles[PITCH] = -pitch;
210 | angles[YAW] = yaw;
211 | angles[ROLL] = 0;
212 | }
213 | fvec3 AxisToAngles(const vec3_t axis[3]) {
214 | vec3_t angles{};
215 | vec3_t right{}, roll_angles{}, tvec{};
216 | fvec3 axisDefault[3] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
217 |
218 |
219 |
220 | // first get the pitch and yaw from the forward vector
221 | vectoangles(axis[0], angles);
222 |
223 | // now get the roll from the right vector
224 | VectorCopy(axis[1], right);
225 | // get the angle difference between the tmpAxis[2] and axis[2] after they have been reverse-rotated
226 | RotatePointAroundVector(tvec, axisDefault[2], right, -angles[YAW]);
227 | RotatePointAroundVector(right, axisDefault[1], tvec, -angles[PITCH]);
228 | // now find the angles, the PITCH is effectively our ROLL
229 | vectoangles(right, roll_angles);
230 | roll_angles[PITCH] = AngleNormalize180(roll_angles[PITCH]);
231 | // if the yaw is more than 90 degrees difference, we should adjust the pitch
232 | if (DotProduct(right, axisDefault[1]) < 0) {
233 | if (roll_angles[PITCH] < 0) {
234 | roll_angles[PITCH] = -90 + (-90 - roll_angles[PITCH]);
235 | }
236 | else {
237 | roll_angles[PITCH] = 90 + (90 - roll_angles[PITCH]);
238 | }
239 | }
240 |
241 | angles[ROLL] = -roll_angles[PITCH];
242 |
243 | return angles;
244 | }
245 |
246 |
247 | float AngleNormalize360(float angle) {
248 | return (360.0f / 65536) * ((int)(angle * (65536 / 360.0f)) & 65535);
249 | }
250 | float AngleNormalize180(float angle) {
251 | angle = AngleNormalize360(angle);
252 | if (angle > 180.0) {
253 | angle -= 360.0;
254 | }
255 | return angle;
256 | }
--------------------------------------------------------------------------------
/com/com_vector.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "global_macros.hpp"
4 |
5 | template
6 | struct vec3;
7 |
8 | using fvec3 = vec3;
9 |
10 | void PlaneFromPointsASM(float* plane, float* v0, float* v1, float* v2);
11 |
12 |
13 | void CrossProduct(const vec3_t v1, const vec3_t v2, vec3_t cross);
14 | vec_t VectorNormalize(vec3_t v); // returns vector length
15 | vec_t VectorNormalize2(const vec3_t v, vec3_t out);
16 | void VectorInverse(vec3_t v);
17 |
18 | void MatrixMultiply(float in1[3][3], float in2[3][3], float out[3][3]);
19 | void PerpendicularVector(vec3_t dst, const vec3_t src);
20 | void RotatePointAroundVector(vec3_t dst, const vec3_t dir, const vec3_t point, float degrees);
21 | void vectoangles(const vec3_t value1, vec3_t angles);
22 |
23 | fvec3 AxisToAngles(const vec3_t axis[3]);
24 |
25 |
26 | float AngleNormalize360(float angle);
27 | float AngleNormalize180(float angle);
--------------------------------------------------------------------------------
/dllmain.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | using namespace std::chrono_literals;
8 |
9 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, [[maybe_unused]] LPVOID lpReserved)
10 | {
11 | std::thread thread;
12 | switch (ul_reason_for_call)
13 | {
14 | case DLL_PROCESS_ATTACH:
15 | DisableThreadLibraryCalls(hModule);
16 |
17 | thread = std::thread([&hModule]()
18 | {
19 | const auto game = GetModuleHandle(MODULE_NAME);
20 |
21 | if (!game) {
22 | return 0;
23 | }
24 |
25 | //FILE* _con = 0;
26 |
27 | //AllocConsole();
28 | //freopen_s(&_con, "CONOUT$", "w", stdout);
29 |
30 | //puts("hello, world!");
31 |
32 |
33 | CG_Init();
34 |
35 | while (!!true) {
36 | std::this_thread::sleep_for(1s);
37 | }
38 |
39 | //if(_con)
40 | // fclose(_con);
41 |
42 | return 1;
43 |
44 | });
45 | thread.detach();
46 |
47 |
48 | break;
49 | case DLL_THREAD_ATTACH:
50 | case DLL_THREAD_DETACH:
51 | case DLL_PROCESS_DETACH:
52 | break;
53 | }
54 | return TRUE;
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/framework.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
4 | // Windows Header Files
5 | #include
6 |
--------------------------------------------------------------------------------
/fs/fs_globals.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "fs_globals.hpp"
3 | #include
4 |
5 | namespace _fs = std::filesystem;
6 |
7 |
8 | std::string fs::exe_file_name()
9 | {
10 | char buffer[MAX_PATH];
11 | GetModuleFileName(NULL, buffer, MAX_PATH);
12 | return std::string(buffer);
13 | }
14 | std::string fs::exe_path()
15 | {
16 | std::string f = exe_file_name();
17 | return f.substr(0, f.find_last_of("\\/"));
18 | }
19 | std::string fs::get_extension(const std::string& file)
20 | {
21 | size_t const extensionPos = file.find_last_of(".");
22 |
23 | if (extensionPos == std::string::npos)
24 | return "";
25 |
26 | return file.substr(extensionPos);
27 | }
28 | std::string fs::previous_directory(std::string& directory)
29 | {
30 | size_t pos = directory.find_last_of('\\');
31 | if (pos < 1 || pos == std::string::npos)
32 | return directory;
33 |
34 | return directory.substr(0, pos);
35 | }
36 | std::string fs::get_file_name(const std::string& fullpath)
37 | {
38 | size_t pos = fullpath.find_last_of('\\');
39 |
40 | if (pos < 1 || pos == std::string::npos)
41 | return fullpath;
42 |
43 | return fullpath.substr(pos + 1);
44 | }
45 | std::string fs::get_file_name_no_extension(const std::string& fullpath)
46 | {
47 | auto file = get_file_name(fullpath);
48 | auto extension = get_extension(file);
49 | return file.substr(0, file.size() - extension.size());
50 |
51 | }
52 |
53 | void fs::create_file(const std::string& path)
54 | {
55 | std::fstream* nf = new std::fstream(path, std::ios_base::out);
56 | *nf << "";
57 | if (nf->is_open())
58 | nf->close();
59 | delete nf;
60 | }
61 | bool fs::create_directory(const std::string& path)
62 | {
63 | return _mkdir((path).c_str()) != -1;
64 | }
65 | std::list fs::files_in_directory(const std::string& path)
66 | {
67 | std::list files;
68 |
69 | if (!_fs::exists(path)) {
70 | return {};
71 | }
72 |
73 | for (const auto& entry : _fs::directory_iterator(path)) {
74 | if (entry.is_directory())
75 | continue;
76 |
77 | std::string str = entry.path().string();
78 | files.push_back(std::move(str));
79 | }
80 |
81 | return (files); //compiler I hope you optimize this!
82 | }
83 | std::string fs::get_last_error()
84 | {
85 | const DWORD errorMessageID = ::GetLastError();
86 | char* messageBuffer = nullptr;
87 |
88 | size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
89 | NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&messageBuffer, 0, NULL);
90 |
91 | std::string output = std::string(messageBuffer, size);
92 |
93 | LocalFree(messageBuffer);
94 | return output;
95 | }
96 | bool fs::valid_file_name(const std::string& name)
97 | {
98 | if (name.empty() || name.length() >= MAX_PATH)
99 | return false;
100 |
101 | for (const auto& i : name) {
102 | if (!std::isalnum(i) && i != '-' && i != '_' && i != ' ')
103 | return false;
104 |
105 | }
106 | return true;
107 | }
--------------------------------------------------------------------------------
/fs/fs_globals.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | namespace fs
10 | {
11 | std::string exe_file_name();
12 | std::string exe_path();
13 | std::string get_extension(const std::string& path);
14 | std::string previous_directory(std::string& directory);
15 | std::string get_file_name(const std::string& fullpath);
16 | std::string get_file_name_no_extension(const std::string& fullpath);
17 |
18 | void create_file(const std::string& path);
19 | bool create_directory(const std::string& path);
20 |
21 | std::list files_in_directory(const std::string& path);
22 |
23 | std::string get_last_error();
24 |
25 | inline bool directory_exists(const std::string& d) { return std::filesystem::exists(d); }
26 | inline bool file_exists(const std::string& f) { return std::filesystem::exists(f); }
27 |
28 | bool valid_file_name(const std::string& name);
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/fs/fs_io.cpp:
--------------------------------------------------------------------------------
1 | #include "fs_io.hpp"
2 | #include "fs_globals.hpp"
3 |
4 | /***********************************************************************
5 | > IOWriter
6 | ***********************************************************************/
7 | bool IOWriter::IO_Write(const std::string& content) const
8 | {
9 |
10 | if (error_occurred)
11 | return false;
12 |
13 | std::ofstream file(filename_, std::ios::out | (binary ? std::ios::binary : NULL));
14 | if (!file.is_open()) {
15 | return false;
16 | }
17 |
18 | IO_WriteStream(file, content);
19 | file.close();
20 | return true;
21 | }
22 | bool IOWriter::IO_Append(const std::string& content) const
23 | {
24 | if (error_occurred)
25 | return false;
26 |
27 | std::ofstream file(filename_, std::ios::app | (binary ? std::ios::binary : NULL));
28 | if (!file.is_open()) {
29 | return false;
30 | }
31 | IO_WriteStream(file, content);
32 | file.close();
33 | return true;
34 | }
35 | bool IOWriter::CreateMissingDirectoriesFromPath(std::string path) const
36 | {
37 | size_t pos = 0;
38 | std::string progress;
39 |
40 | do {
41 |
42 | pos = path.find_first_of('\\');
43 |
44 | if (pos == std::string::npos)
45 | break;
46 |
47 | progress += path.substr(0, pos + 1);
48 | path = path.erase(0, pos + 1);
49 |
50 | progress.pop_back();
51 | if (!fs::directory_exists(progress)) {
52 | if (!fs::create_directory(progress)) {
53 | return false;
54 | }
55 | }
56 | progress += '\\';
57 |
58 | } while (true);
59 |
60 | return true;
61 |
62 | }
63 |
64 | /***********************************************************************
65 | > IOReader
66 | ***********************************************************************/
67 | std::optional IOReader::IO_Read(/*size_t num_bytes*/) const {
68 |
69 | if (error_occurred)
70 | return {};
71 |
72 | std::ifstream file(filename_, std::ios::in | (binary ? std::ios::binary : NULL));
73 | if (!file.is_open()) {
74 | return {};
75 | }
76 |
77 | auto content = IO_ReadStream(file);
78 | file.close();
79 | return content.length() ? std::make_optional(content) : std::nullopt;
80 | }
81 |
82 |
83 | std::string IOReader::IO_ReadStream(std::ifstream& stream) const {
84 | std::string buffer;
85 | try {
86 | char buf[4096];
87 | while (stream.read(buf, sizeof(buf))) {
88 | buffer.append(buf, size_t(stream.gcount()));
89 | }
90 | buffer.append(buf, size_t(stream.gcount()));
91 |
92 | }
93 | catch ([[maybe_unused]] std::ios_base::failure& ex) {
94 | }
95 | return buffer;
96 | }
97 |
98 |
99 | /***********************************************************************
100 | > AgentIO
101 | ***********************************************************************/
102 |
103 | WaWIOWriter::WaWIOWriter(const std::string& relative_path, bool binary)
104 | : IOWriter(fs::exe_path() + '\\' + relative_path, binary) {}
105 |
106 |
107 | WaWIOReader::WaWIOReader(const std::string& relative_path, bool binary)
108 | : IOReader(fs::exe_path() + '\\' + relative_path, binary) {}
109 |
110 |
111 |
--------------------------------------------------------------------------------
/fs/fs_io.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | struct IOItem
8 | {
9 |
10 | IOItem(const std::string& filename, bool in_binary_mode) : filename_(filename), binary(in_binary_mode)
11 | {
12 | }
13 |
14 | constexpr std::string get_target() { return filename_; }
15 |
16 | protected:
17 | std::string filename_;
18 | bool binary = false;
19 | bool error_occurred = false;
20 | };
21 |
22 | struct IOWriter : public IOItem
23 | {
24 | IOWriter(const std::string& filename, bool in_binary_mode) : IOItem(filename, in_binary_mode)
25 | {
26 | error_occurred = !CreateMissingDirectoriesFromPath(filename);
27 | }
28 |
29 | virtual bool IO_Write(const std::string& content) const;
30 | virtual bool IO_Append(const std::string& content) const;
31 |
32 | private:
33 | bool CreateMissingDirectoriesFromPath(std::string path) const;
34 | void IO_WriteStream(std::ofstream& stream, const std::string& content) const {
35 | stream.write(content.data(), content.size());
36 | }
37 | };
38 |
39 | struct IOReader : public IOItem
40 | {
41 | IOReader(const std::string& filename, bool in_binary_mode) : IOItem(filename, in_binary_mode) {}
42 |
43 | virtual std::optional IO_Read(/*size_t num_bytes = std::numeric_limits::max()*/) const;
44 |
45 | private:
46 | std::string IO_ReadStream(std::ifstream& stream) const;
47 |
48 | };
49 |
50 | //these don't belong here but whatever
51 | struct WaWIOWriter : public IOWriter
52 | {
53 | WaWIOWriter(const std::string& relative_path, bool binary = false);
54 | };
55 | struct WaWIOReader : public IOReader
56 | {
57 | WaWIOReader(const std::string& relative_path, bool binary = false);
58 | };
--------------------------------------------------------------------------------
/global_macros.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 |
4 | #define WIN32_LEAN_AND_MEAN
5 |
6 | constexpr auto GAME_NAME = "Call of Duty 5";
7 | constexpr auto MODULE_NAME = "CoDWaWmp.exe";
8 |
9 | #define PI 3.14159265f
10 |
11 | #ifndef M_PI
12 | #define M_PI 3.14159265358979323846f // matches value in gcc v2 math.h
13 | #endif
14 |
15 | typedef float vec_t;
16 | typedef vec_t vec2_t[2];
17 | typedef vec_t vec3_t[3];
18 | typedef vec_t vec4_t[4];
19 |
20 | #define PITCH 0 // up / down
21 | #define YAW 1 // left / right
22 | #define ROLL 2 // fall over
23 |
24 | #define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2])
25 | #define VectorSubtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2])
26 | #define VectorAdd(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2])
27 | #define VectorAddAll(a,b,c) ((c)[0]=(a)[0]+(b),(c)[1]=(a)[1]+(b),(c)[2]=(a)[2]+(b))
28 | #define VectorCopy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2])
29 | #define VectorScale(v, s, o) ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s))
30 | #define VectorScaleAll(a,b,c) ((c)[0]=(a)[0]*(b),(c)[1]=(a)[1]*(b),(c)[2]=(a)[2]*(b))
31 | #define VectorMA(v, s, b, o) ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s))
32 | #define VectorClear( a ) ( ( a )[0] = ( a )[1] = ( a )[2] = 0 )
33 |
34 | #define ANGLE2SHORT( x ) ( (int)( ( x ) * 65536 / 360 ) & 65535 )
35 | #define SHORT2ANGLE( x ) ( ( x ) * ( 360.0f / 65536 ) )
36 |
37 | #define DEG2RAD(a) (((a) * M_PI) / 180.0F)
38 | #define RAD2DEG(a) (((a) * 180.0f) / M_PI)
39 | #define RAD2SHORT(a) ((a) * (32768.f / (float)M_PI))
40 | #define SHORT2RAD(a) ((a) * ((float)M_PI / 32768.f))
41 | #define SHORT2DEG(a) (((a) / 32768.f) * 180.0f)
42 |
43 | #define NOT_SERVER (*(int*)0x0797520 == 0)
44 |
45 | #define NONCOPYABLE(TypeName) \
46 | TypeName(TypeName&&) = delete; \
47 | TypeName(const TypeName&) = delete; \
48 | TypeName& operator=(const TypeName&) = delete; \
49 | TypeName& operator=(TypeName&&) = delete;
50 |
51 |
52 | #define dll_export extern "C" __declspec(dllexport)
53 | #define dll_import extern "C" __declspec(dllimport)
54 | #define class_export class __declspec(dllexport)
55 | #define class_import class __declspec(dllimport)
56 |
57 | #define abstract_class class
58 |
59 | #define WASD_PRESSED() \
60 | ((GetAsyncKeyState('W') < 0) || \
61 | (GetAsyncKeyState('A') < 0) || \
62 | (GetAsyncKeyState('S') < 0) || \
63 | (GetAsyncKeyState('D') < 0))
64 |
65 | //template
66 | //using is_any_pointer = std::disjunction<
67 | // std::is_integral>,
68 | // std::is_pointer
69 | //>;
--------------------------------------------------------------------------------
/mh/MinHook.h:
--------------------------------------------------------------------------------
1 | /*
2 | * MinHook - The Minimalistic API Hooking Library for x64/x86
3 | * Copyright (C) 2009-2017 Tsuda Kageyu.
4 | * All rights reserved.
5 | *
6 | * Redistribution and use in source and binary forms, with or without
7 | * modification, are permitted provided that the following conditions
8 | * are met:
9 | *
10 | * 1. Redistributions of source code must retain the above copyright
11 | * notice, this list of conditions and the following disclaimer.
12 | * 2. Redistributions in binary form must reproduce the above copyright
13 | * notice, this list of conditions and the following disclaimer in the
14 | * documentation and/or other materials provided with the distribution.
15 | *
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
20 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | #define MINHOOK_LIBRARY
30 |
31 | #pragma once
32 |
33 | #if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__)
34 | #error MinHook supports only x86 and x64 systems.
35 | #endif
36 |
37 | #include
38 |
39 | // MinHook Error Codes.
40 | typedef enum MH_STATUS
41 | {
42 | // Unknown error. Should not be returned.
43 | MH_UNKNOWN = -1,
44 |
45 | // Successful.
46 | MH_OK = 0,
47 |
48 | // MinHook is already initialized.
49 | MH_ERROR_ALREADY_INITIALIZED,
50 |
51 | // MinHook is not initialized yet, or already uninitialized.
52 | MH_ERROR_NOT_INITIALIZED,
53 |
54 | // The hook for the specified target function is already created.
55 | MH_ERROR_ALREADY_CREATED,
56 |
57 | // The hook for the specified target function is not created yet.
58 | MH_ERROR_NOT_CREATED,
59 |
60 | // The hook for the specified target function is already enabled.
61 | MH_ERROR_ENABLED,
62 |
63 | // The hook for the specified target function is not enabled yet, or already
64 | // disabled.
65 | MH_ERROR_DISABLED,
66 |
67 | // The specified pointer is invalid. It points the address of non-allocated
68 | // and/or non-executable region.
69 | MH_ERROR_NOT_EXECUTABLE,
70 |
71 | // The specified target function cannot be hooked.
72 | MH_ERROR_UNSUPPORTED_FUNCTION,
73 |
74 | // Failed to allocate memory.
75 | MH_ERROR_MEMORY_ALLOC,
76 |
77 | // Failed to change the memory protection.
78 | MH_ERROR_MEMORY_PROTECT,
79 |
80 | // The specified module is not loaded.
81 | MH_ERROR_MODULE_NOT_FOUND,
82 |
83 | // The specified function is not found.
84 | MH_ERROR_FUNCTION_NOT_FOUND
85 | }
86 | MH_STATUS;
87 |
88 | // Can be passed as a parameter to MH_EnableHook, MH_DisableHook,
89 | // MH_QueueEnableHook or MH_QueueDisableHook.
90 | #define MH_ALL_HOOKS NULL
91 |
92 | #ifdef __cplusplus
93 | extern "C" {
94 | #endif
95 |
96 | // Initialize the MinHook library. You must call this function EXACTLY ONCE
97 | // at the beginning of your program.
98 | MH_STATUS WINAPI MH_Initialize(VOID);
99 |
100 | // Uninitialize the MinHook library. You must call this function EXACTLY
101 | // ONCE at the end of your program.
102 | MH_STATUS WINAPI MH_Uninitialize(VOID);
103 |
104 | // Creates a Hook for the specified target function, in disabled state.
105 | // Parameters:
106 | // pTarget [in] A pointer to the target function, which will be
107 | // overridden by the detour function.
108 | // pDetour [in] A pointer to the detour function, which will override
109 | // the target function.
110 | // ppOriginal [out] A pointer to the trampoline function, which will be
111 | // used to call the original target function.
112 | // This parameter can be NULL.
113 | MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal);
114 |
115 | // Creates a Hook for the specified API function, in disabled state.
116 | // Parameters:
117 | // pszModule [in] A pointer to the loaded module name which contains the
118 | // target function.
119 | // pszTarget [in] A pointer to the target function name, which will be
120 | // overridden by the detour function.
121 | // pDetour [in] A pointer to the detour function, which will override
122 | // the target function.
123 | // ppOriginal [out] A pointer to the trampoline function, which will be
124 | // used to call the original target function.
125 | // This parameter can be NULL.
126 | MH_STATUS WINAPI MH_CreateHookApi(
127 | LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal);
128 |
129 | // Creates a Hook for the specified API function, in disabled state.
130 | // Parameters:
131 | // pszModule [in] A pointer to the loaded module name which contains the
132 | // target function.
133 | // pszTarget [in] A pointer to the target function name, which will be
134 | // overridden by the detour function.
135 | // pDetour [in] A pointer to the detour function, which will override
136 | // the target function.
137 | // ppOriginal [out] A pointer to the trampoline function, which will be
138 | // used to call the original target function.
139 | // This parameter can be NULL.
140 | // ppTarget [out] A pointer to the target function, which will be used
141 | // with other functions.
142 | // This parameter can be NULL.
143 | MH_STATUS WINAPI MH_CreateHookApiEx(
144 | LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget);
145 |
146 | // Removes an already created hook.
147 | // Parameters:
148 | // pTarget [in] A pointer to the target function.
149 | MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget);
150 |
151 | // Enables an already created hook.
152 | // Parameters:
153 | // pTarget [in] A pointer to the target function.
154 | // If this parameter is MH_ALL_HOOKS, all created hooks are
155 | // enabled in one go.
156 | MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget);
157 |
158 | // Disables an already created hook.
159 | // Parameters:
160 | // pTarget [in] A pointer to the target function.
161 | // If this parameter is MH_ALL_HOOKS, all created hooks are
162 | // disabled in one go.
163 | MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget);
164 |
165 | // Queues to enable an already created hook.
166 | // Parameters:
167 | // pTarget [in] A pointer to the target function.
168 | // If this parameter is MH_ALL_HOOKS, all created hooks are
169 | // queued to be enabled.
170 | MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget);
171 |
172 | // Queues to disable an already created hook.
173 | // Parameters:
174 | // pTarget [in] A pointer to the target function.
175 | // If this parameter is MH_ALL_HOOKS, all created hooks are
176 | // queued to be disabled.
177 | MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget);
178 |
179 | // Applies all queued changes in one go.
180 | MH_STATUS WINAPI MH_ApplyQueued(VOID);
181 |
182 | // Translates the MH_STATUS to its name as a string.
183 | const char * WINAPI MH_StatusToString(MH_STATUS status);
184 |
185 | #ifdef __cplusplus
186 | }
187 | #endif
188 |
189 |
--------------------------------------------------------------------------------
/mh/buffer.c:
--------------------------------------------------------------------------------
1 | /*
2 | * MinHook - The Minimalistic API Hooking Library for x64/x86
3 | * Copyright (C) 2009-2017 Tsuda Kageyu.
4 | * All rights reserved.
5 | *
6 | * Redistribution and use in source and binary forms, with or without
7 | * modification, are permitted provided that the following conditions
8 | * are met:
9 | *
10 | * 1. Redistributions of source code must retain the above copyright
11 | * notice, this list of conditions and the following disclaimer.
12 | * 2. Redistributions in binary form must reproduce the above copyright
13 | * notice, this list of conditions and the following disclaimer in the
14 | * documentation and/or other materials provided with the distribution.
15 | *
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
20 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | #pragma warning(push)
30 | #pragma warning(disable:4100)
31 | #pragma warning(disable:4201)
32 |
33 | #include
34 | #include "buffer.h"
35 |
36 | // Size of each memory block. (= page size of VirtualAlloc)
37 | #define MEMORY_BLOCK_SIZE 0x1000
38 |
39 | // Max range for seeking a memory block. (= 1024MB)
40 | #define MAX_MEMORY_RANGE 0x40000000
41 |
42 | // Memory protection flags to check the executable address.
43 | #define PAGE_EXECUTE_FLAGS \
44 | (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)
45 |
46 | // Memory slot.
47 | typedef struct _MEMORY_SLOT
48 | {
49 | union
50 | {
51 | struct _MEMORY_SLOT *pNext;
52 | UINT8 buffer[MEMORY_SLOT_SIZE];
53 | };
54 | } MEMORY_SLOT, *PMEMORY_SLOT;
55 |
56 | // Memory block info. Placed at the head of each block.
57 | typedef struct _MEMORY_BLOCK
58 | {
59 | struct _MEMORY_BLOCK *pNext;
60 | PMEMORY_SLOT pFree; // First element of the free slot list.
61 | UINT usedCount;
62 | } MEMORY_BLOCK, *PMEMORY_BLOCK;
63 |
64 | //-------------------------------------------------------------------------
65 | // Global Variables:
66 | //-------------------------------------------------------------------------
67 |
68 | // First element of the memory block list.
69 | PMEMORY_BLOCK g_pMemoryBlocks;
70 |
71 | //-------------------------------------------------------------------------
72 | VOID InitializeBuffer(VOID)
73 | {
74 | // Nothing to do for now.
75 | }
76 |
77 | //-------------------------------------------------------------------------
78 | VOID UninitializeBuffer(VOID)
79 | {
80 | PMEMORY_BLOCK pBlock = g_pMemoryBlocks;
81 | g_pMemoryBlocks = NULL;
82 |
83 | while (pBlock)
84 | {
85 | PMEMORY_BLOCK pNext = pBlock->pNext;
86 | VirtualFree(pBlock, 0, MEM_RELEASE);
87 | pBlock = pNext;
88 | }
89 | }
90 |
91 | //-------------------------------------------------------------------------
92 | #if defined(_M_X64) || defined(__x86_64__)
93 | static LPVOID FindPrevFreeRegion(LPVOID pAddress, LPVOID pMinAddr, DWORD dwAllocationGranularity)
94 | {
95 | ULONG_PTR tryAddr = (ULONG_PTR)pAddress;
96 |
97 | // Round down to the allocation granularity.
98 | tryAddr -= tryAddr % dwAllocationGranularity;
99 |
100 | // Start from the previous allocation granularity multiply.
101 | tryAddr -= dwAllocationGranularity;
102 |
103 | while (tryAddr >= (ULONG_PTR)pMinAddr)
104 | {
105 | MEMORY_BASIC_INFORMATION mbi;
106 | if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0)
107 | break;
108 |
109 | if (mbi.State == MEM_FREE)
110 | return (LPVOID)tryAddr;
111 |
112 | if ((ULONG_PTR)mbi.AllocationBase < dwAllocationGranularity)
113 | break;
114 |
115 | tryAddr = (ULONG_PTR)mbi.AllocationBase - dwAllocationGranularity;
116 | }
117 |
118 | return NULL;
119 | }
120 | #endif
121 |
122 | //-------------------------------------------------------------------------
123 | #if defined(_M_X64) || defined(__x86_64__)
124 | static LPVOID FindNextFreeRegion(LPVOID pAddress, LPVOID pMaxAddr, DWORD dwAllocationGranularity)
125 | {
126 | ULONG_PTR tryAddr = (ULONG_PTR)pAddress;
127 |
128 | // Round down to the allocation granularity.
129 | tryAddr -= tryAddr % dwAllocationGranularity;
130 |
131 | // Start from the next allocation granularity multiply.
132 | tryAddr += dwAllocationGranularity;
133 |
134 | while (tryAddr <= (ULONG_PTR)pMaxAddr)
135 | {
136 | MEMORY_BASIC_INFORMATION mbi;
137 | if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0)
138 | break;
139 |
140 | if (mbi.State == MEM_FREE)
141 | return (LPVOID)tryAddr;
142 |
143 | tryAddr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize;
144 |
145 | // Round up to the next allocation granularity.
146 | tryAddr += dwAllocationGranularity - 1;
147 | tryAddr -= tryAddr % dwAllocationGranularity;
148 | }
149 |
150 | return NULL;
151 | }
152 | #endif
153 |
154 | //-------------------------------------------------------------------------
155 | static PMEMORY_BLOCK GetMemoryBlock(LPVOID pOrigin)
156 | {
157 | PMEMORY_BLOCK pBlock;
158 | #if defined(_M_X64) || defined(__x86_64__)
159 | ULONG_PTR minAddr;
160 | ULONG_PTR maxAddr;
161 |
162 | SYSTEM_INFO si;
163 | GetSystemInfo(&si);
164 | minAddr = (ULONG_PTR)si.lpMinimumApplicationAddress;
165 | maxAddr = (ULONG_PTR)si.lpMaximumApplicationAddress;
166 |
167 | // pOrigin ± 512MB
168 | if ((ULONG_PTR)pOrigin > MAX_MEMORY_RANGE && minAddr < (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE)
169 | minAddr = (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE;
170 |
171 | if (maxAddr > (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE)
172 | maxAddr = (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE;
173 |
174 | // Make room for MEMORY_BLOCK_SIZE bytes.
175 | maxAddr -= MEMORY_BLOCK_SIZE - 1;
176 | #endif
177 |
178 | // Look the registered blocks for a reachable one.
179 | for (pBlock = g_pMemoryBlocks; pBlock != NULL; pBlock = pBlock->pNext)
180 | {
181 | #if defined(_M_X64) || defined(__x86_64__)
182 | // Ignore the blocks too far.
183 | if ((ULONG_PTR)pBlock < minAddr || (ULONG_PTR)pBlock >= maxAddr)
184 | continue;
185 | #endif
186 | // The block has at least one unused slot.
187 | if (pBlock->pFree != NULL)
188 | return pBlock;
189 | }
190 |
191 | #if defined(_M_X64) || defined(__x86_64__)
192 | // Alloc a new block above if not found.
193 | {
194 | LPVOID pAlloc = pOrigin;
195 | while ((ULONG_PTR)pAlloc >= minAddr)
196 | {
197 | pAlloc = FindPrevFreeRegion(pAlloc, (LPVOID)minAddr, si.dwAllocationGranularity);
198 | if (pAlloc == NULL)
199 | break;
200 |
201 | pBlock = (PMEMORY_BLOCK)VirtualAlloc(
202 | pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
203 | if (pBlock != NULL)
204 | break;
205 | }
206 | }
207 |
208 | // Alloc a new block below if not found.
209 | if (pBlock == NULL)
210 | {
211 | LPVOID pAlloc = pOrigin;
212 | while ((ULONG_PTR)pAlloc <= maxAddr)
213 | {
214 | pAlloc = FindNextFreeRegion(pAlloc, (LPVOID)maxAddr, si.dwAllocationGranularity);
215 | if (pAlloc == NULL)
216 | break;
217 |
218 | pBlock = (PMEMORY_BLOCK)VirtualAlloc(
219 | pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
220 | if (pBlock != NULL)
221 | break;
222 | }
223 | }
224 | #else
225 | // In x86 mode, a memory block can be placed anywhere.
226 | pBlock = (PMEMORY_BLOCK)VirtualAlloc(
227 | NULL, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
228 | #endif
229 |
230 | if (pBlock != NULL)
231 | {
232 | // Build a linked list of all the slots.
233 | PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBlock + 1;
234 | pBlock->pFree = NULL;
235 | pBlock->usedCount = 0;
236 | do
237 | {
238 | pSlot->pNext = pBlock->pFree;
239 | pBlock->pFree = pSlot;
240 | pSlot++;
241 | } while ((ULONG_PTR)pSlot - (ULONG_PTR)pBlock <= MEMORY_BLOCK_SIZE - MEMORY_SLOT_SIZE);
242 |
243 | pBlock->pNext = g_pMemoryBlocks;
244 | g_pMemoryBlocks = pBlock;
245 | }
246 |
247 | return pBlock;
248 | }
249 |
250 | //-------------------------------------------------------------------------
251 | LPVOID AllocateBuffer(LPVOID pOrigin)
252 | {
253 | PMEMORY_SLOT pSlot;
254 | PMEMORY_BLOCK pBlock = GetMemoryBlock(pOrigin);
255 | if (pBlock == NULL)
256 | return NULL;
257 |
258 | // Remove an unused slot from the list.
259 | pSlot = pBlock->pFree;
260 | pBlock->pFree = pSlot->pNext;
261 | pBlock->usedCount++;
262 | #ifdef _DEBUG
263 | // Fill the slot with INT3 for debugging.
264 | memset(pSlot, 0xCC, sizeof(MEMORY_SLOT));
265 | #endif
266 | return pSlot;
267 | }
268 |
269 | //-------------------------------------------------------------------------
270 | VOID FreeBuffer(LPVOID pBuffer)
271 | {
272 | PMEMORY_BLOCK pBlock = g_pMemoryBlocks;
273 | PMEMORY_BLOCK pPrev = NULL;
274 | ULONG_PTR pTargetBlock = ((ULONG_PTR)pBuffer / MEMORY_BLOCK_SIZE) * MEMORY_BLOCK_SIZE;
275 |
276 | while (pBlock != NULL)
277 | {
278 | if ((ULONG_PTR)pBlock == pTargetBlock)
279 | {
280 | PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBuffer;
281 | #ifdef _DEBUG
282 | // Clear the released slot for debugging.
283 | memset(pSlot, 0x00, sizeof(*pSlot));
284 | #endif
285 | // Restore the released slot to the list.
286 | pSlot->pNext = pBlock->pFree;
287 | pBlock->pFree = pSlot;
288 | pBlock->usedCount--;
289 |
290 | // Free if unused.
291 | if (pBlock->usedCount == 0)
292 | {
293 | if (pPrev)
294 | pPrev->pNext = pBlock->pNext;
295 | else
296 | g_pMemoryBlocks = pBlock->pNext;
297 |
298 | VirtualFree(pBlock, 0, MEM_RELEASE);
299 | }
300 |
301 | break;
302 | }
303 |
304 | pPrev = pBlock;
305 | pBlock = pBlock->pNext;
306 | }
307 | }
308 |
309 | //-------------------------------------------------------------------------
310 | BOOL IsExecutableAddress(LPVOID pAddress)
311 | {
312 | MEMORY_BASIC_INFORMATION mi;
313 | VirtualQuery(pAddress, &mi, sizeof(mi));
314 |
315 | return (mi.State == MEM_COMMIT && (mi.Protect & PAGE_EXECUTE_FLAGS));
316 | }
317 |
318 | #pragma warning(pop)
319 |
--------------------------------------------------------------------------------
/mh/buffer.h:
--------------------------------------------------------------------------------
1 | /*
2 | * MinHook - The Minimalistic API Hooking Library for x64/x86
3 | * Copyright (C) 2009-2017 Tsuda Kageyu.
4 | * All rights reserved.
5 | *
6 | * Redistribution and use in source and binary forms, with or without
7 | * modification, are permitted provided that the following conditions
8 | * are met:
9 | *
10 | * 1. Redistributions of source code must retain the above copyright
11 | * notice, this list of conditions and the following disclaimer.
12 | * 2. Redistributions in binary form must reproduce the above copyright
13 | * notice, this list of conditions and the following disclaimer in the
14 | * documentation and/or other materials provided with the distribution.
15 | *
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
20 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | #pragma once
30 |
31 | // Size of each memory slot.
32 | #if defined(_M_X64) || defined(__x86_64__)
33 | #define MEMORY_SLOT_SIZE 64
34 | #else
35 | #define MEMORY_SLOT_SIZE 32
36 | #endif
37 |
38 | VOID InitializeBuffer(VOID);
39 | VOID UninitializeBuffer(VOID);
40 | LPVOID AllocateBuffer(LPVOID pOrigin);
41 | VOID FreeBuffer(LPVOID pBuffer);
42 | BOOL IsExecutableAddress(LPVOID pAddress);
43 |
--------------------------------------------------------------------------------
/mh/hde/hde32.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Hacker Disassembler Engine 32 C
3 | * Copyright (c) 2008-2009, Vyacheslav Patkov.
4 | * All rights reserved.
5 | *
6 | */
7 |
8 | #if defined(_M_IX86) || defined(__i386__)
9 |
10 | #include "hde32.h"
11 | #include "table32.h"
12 |
13 | unsigned int hde32_disasm(const void *code, hde32s *hs)
14 | {
15 | uint8_t x, c=0, *p = (uint8_t *)code, cflags, opcode, pref = 0;
16 | uint8_t *ht = hde32_table, m_mod, m_reg, m_rm, disp_size = 0;
17 |
18 | // Avoid using memset to reduce the footprint.
19 | #ifndef _MSC_VER
20 | memset((LPBYTE)hs, 0, sizeof(hde32s));
21 | #else
22 | __stosb((LPBYTE)hs, 0, sizeof(hde32s));
23 | #endif
24 |
25 | for (x = 16; x; x--)
26 | switch (c = *p++) {
27 | case 0xf3:
28 | hs->p_rep = c;
29 | pref |= PRE_F3;
30 | break;
31 | case 0xf2:
32 | hs->p_rep = c;
33 | pref |= PRE_F2;
34 | break;
35 | case 0xf0:
36 | hs->p_lock = c;
37 | pref |= PRE_LOCK;
38 | break;
39 | case 0x26: case 0x2e: case 0x36:
40 | case 0x3e: case 0x64: case 0x65:
41 | hs->p_seg = c;
42 | pref |= PRE_SEG;
43 | break;
44 | case 0x66:
45 | hs->p_66 = c;
46 | pref |= PRE_66;
47 | break;
48 | case 0x67:
49 | hs->p_67 = c;
50 | pref |= PRE_67;
51 | break;
52 | default:
53 | goto pref_done;
54 | }
55 | pref_done:
56 |
57 | hs->flags = (uint32_t)pref << 23;
58 |
59 | if (!pref)
60 | pref |= PRE_NONE;
61 |
62 | if ((hs->opcode = c) == 0x0f) {
63 | hs->opcode2 = c = *p++;
64 | ht += DELTA_OPCODES;
65 | } else if (c >= 0xa0 && c <= 0xa3) {
66 | if (pref & PRE_67)
67 | pref |= PRE_66;
68 | else
69 | pref &= ~PRE_66;
70 | }
71 |
72 | opcode = c;
73 | cflags = ht[ht[opcode / 4] + (opcode % 4)];
74 |
75 | if (cflags == C_ERROR) {
76 | hs->flags |= F_ERROR | F_ERROR_OPCODE;
77 | cflags = 0;
78 | if ((opcode & -3) == 0x24)
79 | cflags++;
80 | }
81 |
82 | x = 0;
83 | if (cflags & C_GROUP) {
84 | uint16_t t;
85 | t = *(uint16_t *)(ht + (cflags & 0x7f));
86 | cflags = (uint8_t)t;
87 | x = (uint8_t)(t >> 8);
88 | }
89 |
90 | if (hs->opcode2) {
91 | ht = hde32_table + DELTA_PREFIXES;
92 | if (ht[ht[opcode / 4] + (opcode % 4)] & pref)
93 | hs->flags |= F_ERROR | F_ERROR_OPCODE;
94 | }
95 |
96 | if (cflags & C_MODRM) {
97 | hs->flags |= F_MODRM;
98 | hs->modrm = c = *p++;
99 | hs->modrm_mod = m_mod = c >> 6;
100 | hs->modrm_rm = m_rm = c & 7;
101 | hs->modrm_reg = m_reg = (c & 0x3f) >> 3;
102 |
103 | if (x && ((x << m_reg) & 0x80))
104 | hs->flags |= F_ERROR | F_ERROR_OPCODE;
105 |
106 | if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) {
107 | uint8_t t = opcode - 0xd9;
108 | if (m_mod == 3) {
109 | ht = hde32_table + DELTA_FPU_MODRM + t*8;
110 | t = ht[m_reg] << m_rm;
111 | } else {
112 | ht = hde32_table + DELTA_FPU_REG;
113 | t = ht[t] << m_reg;
114 | }
115 | if (t & 0x80)
116 | hs->flags |= F_ERROR | F_ERROR_OPCODE;
117 | }
118 |
119 | if (pref & PRE_LOCK) {
120 | if (m_mod == 3) {
121 | hs->flags |= F_ERROR | F_ERROR_LOCK;
122 | } else {
123 | uint8_t *table_end, op = opcode;
124 | if (hs->opcode2) {
125 | ht = hde32_table + DELTA_OP2_LOCK_OK;
126 | table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK;
127 | } else {
128 | ht = hde32_table + DELTA_OP_LOCK_OK;
129 | table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK;
130 | op &= -2;
131 | }
132 | for (; ht != table_end; ht++)
133 | if (*ht++ == op) {
134 | if (!((*ht << m_reg) & 0x80))
135 | goto no_lock_error;
136 | else
137 | break;
138 | }
139 | hs->flags |= F_ERROR | F_ERROR_LOCK;
140 | no_lock_error:
141 | ;
142 | }
143 | }
144 |
145 | if (hs->opcode2) {
146 | switch (opcode) {
147 | case 0x20: case 0x22:
148 | m_mod = 3;
149 | if (m_reg > 4 || m_reg == 1)
150 | goto error_operand;
151 | else
152 | goto no_error_operand;
153 | case 0x21: case 0x23:
154 | m_mod = 3;
155 | if (m_reg == 4 || m_reg == 5)
156 | goto error_operand;
157 | else
158 | goto no_error_operand;
159 | }
160 | } else {
161 | switch (opcode) {
162 | case 0x8c:
163 | if (m_reg > 5)
164 | goto error_operand;
165 | else
166 | goto no_error_operand;
167 | case 0x8e:
168 | if (m_reg == 1 || m_reg > 5)
169 | goto error_operand;
170 | else
171 | goto no_error_operand;
172 | }
173 | }
174 |
175 | if (m_mod == 3) {
176 | uint8_t *table_end;
177 | if (hs->opcode2) {
178 | ht = hde32_table + DELTA_OP2_ONLY_MEM;
179 | table_end = ht + sizeof(hde32_table) - DELTA_OP2_ONLY_MEM;
180 | } else {
181 | ht = hde32_table + DELTA_OP_ONLY_MEM;
182 | table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM;
183 | }
184 | for (; ht != table_end; ht += 2)
185 | if (*ht++ == opcode) {
186 | if (*ht++ & pref && !((*ht << m_reg) & 0x80))
187 | goto error_operand;
188 | else
189 | break;
190 | }
191 | goto no_error_operand;
192 | } else if (hs->opcode2) {
193 | switch (opcode) {
194 | case 0x50: case 0xd7: case 0xf7:
195 | if (pref & (PRE_NONE | PRE_66))
196 | goto error_operand;
197 | break;
198 | case 0xd6:
199 | if (pref & (PRE_F2 | PRE_F3))
200 | goto error_operand;
201 | break;
202 | case 0xc5:
203 | goto error_operand;
204 | }
205 | goto no_error_operand;
206 | } else
207 | goto no_error_operand;
208 |
209 | error_operand:
210 | hs->flags |= F_ERROR | F_ERROR_OPERAND;
211 | no_error_operand:
212 |
213 | c = *p++;
214 | if (m_reg <= 1) {
215 | if (opcode == 0xf6)
216 | cflags |= C_IMM8;
217 | else if (opcode == 0xf7)
218 | cflags |= C_IMM_P66;
219 | }
220 |
221 | switch (m_mod) {
222 | case 0:
223 | if (pref & PRE_67) {
224 | if (m_rm == 6)
225 | disp_size = 2;
226 | } else
227 | if (m_rm == 5)
228 | disp_size = 4;
229 | break;
230 | case 1:
231 | disp_size = 1;
232 | break;
233 | case 2:
234 | disp_size = 2;
235 | if (!(pref & PRE_67))
236 | disp_size <<= 1;
237 | }
238 |
239 | if (m_mod != 3 && m_rm == 4 && !(pref & PRE_67)) {
240 | hs->flags |= F_SIB;
241 | p++;
242 | hs->sib = c;
243 | hs->sib_scale = c >> 6;
244 | hs->sib_index = (c & 0x3f) >> 3;
245 | if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1))
246 | disp_size = 4;
247 | }
248 |
249 | p--;
250 | switch (disp_size) {
251 | case 1:
252 | hs->flags |= F_DISP8;
253 | hs->disp.disp8 = *p;
254 | break;
255 | case 2:
256 | hs->flags |= F_DISP16;
257 | hs->disp.disp16 = *(uint16_t *)p;
258 | break;
259 | case 4:
260 | hs->flags |= F_DISP32;
261 | hs->disp.disp32 = *(uint32_t *)p;
262 | }
263 | p += disp_size;
264 | } else if (pref & PRE_LOCK)
265 | hs->flags |= F_ERROR | F_ERROR_LOCK;
266 |
267 | if (cflags & C_IMM_P66) {
268 | if (cflags & C_REL32) {
269 | if (pref & PRE_66) {
270 | hs->flags |= F_IMM16 | F_RELATIVE;
271 | hs->imm.imm16 = *(uint16_t *)p;
272 | p += 2;
273 | goto disasm_done;
274 | }
275 | goto rel32_ok;
276 | }
277 | if (pref & PRE_66) {
278 | hs->flags |= F_IMM16;
279 | hs->imm.imm16 = *(uint16_t *)p;
280 | p += 2;
281 | } else {
282 | hs->flags |= F_IMM32;
283 | hs->imm.imm32 = *(uint32_t *)p;
284 | p += 4;
285 | }
286 | }
287 |
288 | if (cflags & C_IMM16) {
289 | if (hs->flags & F_IMM32) {
290 | hs->flags |= F_IMM16;
291 | hs->disp.disp16 = *(uint16_t *)p;
292 | } else if (hs->flags & F_IMM16) {
293 | hs->flags |= F_2IMM16;
294 | hs->disp.disp16 = *(uint16_t *)p;
295 | } else {
296 | hs->flags |= F_IMM16;
297 | hs->imm.imm16 = *(uint16_t *)p;
298 | }
299 | p += 2;
300 | }
301 | if (cflags & C_IMM8) {
302 | hs->flags |= F_IMM8;
303 | hs->imm.imm8 = *p++;
304 | }
305 |
306 | if (cflags & C_REL32) {
307 | rel32_ok:
308 | hs->flags |= F_IMM32 | F_RELATIVE;
309 | hs->imm.imm32 = *(uint32_t *)p;
310 | p += 4;
311 | } else if (cflags & C_REL8) {
312 | hs->flags |= F_IMM8 | F_RELATIVE;
313 | hs->imm.imm8 = *p++;
314 | }
315 |
316 | disasm_done:
317 |
318 | if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) {
319 | hs->flags |= F_ERROR | F_ERROR_LENGTH;
320 | hs->len = 15;
321 | }
322 |
323 | return (unsigned int)hs->len;
324 | }
325 |
326 | #endif // defined(_M_IX86) || defined(__i386__)
327 |
--------------------------------------------------------------------------------
/mh/hde/hde32.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Hacker Disassembler Engine 32
3 | * Copyright (c) 2006-2009, Vyacheslav Patkov.
4 | * All rights reserved.
5 | *
6 | * hde32.h: C/C++ header file
7 | *
8 | */
9 |
10 | #ifndef _HDE32_H_
11 | #define _HDE32_H_
12 |
13 | /* stdint.h - C99 standard header
14 | * http://en.wikipedia.org/wiki/stdint.h
15 | *
16 | * if your compiler doesn't contain "stdint.h" header (for
17 | * example, Microsoft Visual C++), you can download file:
18 | * http://www.azillionmonkeys.com/qed/pstdint.h
19 | * and change next line to:
20 | * #include "pstdint.h"
21 | */
22 | #include "pstdint.h"
23 |
24 | #define F_MODRM 0x00000001
25 | #define F_SIB 0x00000002
26 | #define F_IMM8 0x00000004
27 | #define F_IMM16 0x00000008
28 | #define F_IMM32 0x00000010
29 | #define F_DISP8 0x00000020
30 | #define F_DISP16 0x00000040
31 | #define F_DISP32 0x00000080
32 | #define F_RELATIVE 0x00000100
33 | #define F_2IMM16 0x00000800
34 | #define F_ERROR 0x00001000
35 | #define F_ERROR_OPCODE 0x00002000
36 | #define F_ERROR_LENGTH 0x00004000
37 | #define F_ERROR_LOCK 0x00008000
38 | #define F_ERROR_OPERAND 0x00010000
39 | #define F_PREFIX_REPNZ 0x01000000
40 | #define F_PREFIX_REPX 0x02000000
41 | #define F_PREFIX_REP 0x03000000
42 | #define F_PREFIX_66 0x04000000
43 | #define F_PREFIX_67 0x08000000
44 | #define F_PREFIX_LOCK 0x10000000
45 | #define F_PREFIX_SEG 0x20000000
46 | #define F_PREFIX_ANY 0x3f000000
47 |
48 | #define PREFIX_SEGMENT_CS 0x2e
49 | #define PREFIX_SEGMENT_SS 0x36
50 | #define PREFIX_SEGMENT_DS 0x3e
51 | #define PREFIX_SEGMENT_ES 0x26
52 | #define PREFIX_SEGMENT_FS 0x64
53 | #define PREFIX_SEGMENT_GS 0x65
54 | #define PREFIX_LOCK 0xf0
55 | #define PREFIX_REPNZ 0xf2
56 | #define PREFIX_REPX 0xf3
57 | #define PREFIX_OPERAND_SIZE 0x66
58 | #define PREFIX_ADDRESS_SIZE 0x67
59 |
60 | #pragma pack(push,1)
61 |
62 | typedef struct {
63 | uint8_t len;
64 | uint8_t p_rep;
65 | uint8_t p_lock;
66 | uint8_t p_seg;
67 | uint8_t p_66;
68 | uint8_t p_67;
69 | uint8_t opcode;
70 | uint8_t opcode2;
71 | uint8_t modrm;
72 | uint8_t modrm_mod;
73 | uint8_t modrm_reg;
74 | uint8_t modrm_rm;
75 | uint8_t sib;
76 | uint8_t sib_scale;
77 | uint8_t sib_index;
78 | uint8_t sib_base;
79 | union {
80 | uint8_t imm8;
81 | uint16_t imm16;
82 | uint32_t imm32;
83 | } imm;
84 | union {
85 | uint8_t disp8;
86 | uint16_t disp16;
87 | uint32_t disp32;
88 | } disp;
89 | uint32_t flags;
90 | } hde32s;
91 |
92 | #pragma pack(pop)
93 |
94 | #ifdef __cplusplus
95 | extern "C" {
96 | #endif
97 |
98 | /* __cdecl */
99 | unsigned int hde32_disasm(const void *code, hde32s *hs);
100 |
101 | #ifdef __cplusplus
102 | }
103 | #endif
104 |
105 | #endif /* _HDE32_H_ */
106 |
--------------------------------------------------------------------------------
/mh/hde/hde64.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Hacker Disassembler Engine 64 C
3 | * Copyright (c) 2008-2009, Vyacheslav Patkov.
4 | * All rights reserved.
5 | *
6 | */
7 |
8 | #pragma warning(disable : 4206)
9 |
10 |
11 | #if defined(_M_X64) || defined(__x86_64__)
12 |
13 | #include "hde64.h"
14 | #include "table64.h"
15 |
16 | unsigned int hde64_disasm(const void *code, hde64s *hs)
17 | {
18 | uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0;
19 | uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0;
20 | uint8_t op64 = 0;
21 |
22 | // Avoid using memset to reduce the footprint.
23 | #ifndef _MSC_VER
24 | memset((LPBYTE)hs, 0, sizeof(hde64s));
25 | #else
26 | __stosb((LPBYTE)hs, 0, sizeof(hde64s));
27 | #endif
28 |
29 | for (x = 16; x; x--)
30 | switch (c = *p++) {
31 | case 0xf3:
32 | hs->p_rep = c;
33 | pref |= PRE_F3;
34 | break;
35 | case 0xf2:
36 | hs->p_rep = c;
37 | pref |= PRE_F2;
38 | break;
39 | case 0xf0:
40 | hs->p_lock = c;
41 | pref |= PRE_LOCK;
42 | break;
43 | case 0x26: case 0x2e: case 0x36:
44 | case 0x3e: case 0x64: case 0x65:
45 | hs->p_seg = c;
46 | pref |= PRE_SEG;
47 | break;
48 | case 0x66:
49 | hs->p_66 = c;
50 | pref |= PRE_66;
51 | break;
52 | case 0x67:
53 | hs->p_67 = c;
54 | pref |= PRE_67;
55 | break;
56 | default:
57 | goto pref_done;
58 | }
59 | pref_done:
60 |
61 | hs->flags = (uint32_t)pref << 23;
62 |
63 | if (!pref)
64 | pref |= PRE_NONE;
65 |
66 | if ((c & 0xf0) == 0x40) {
67 | hs->flags |= F_PREFIX_REX;
68 | if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8)
69 | op64++;
70 | hs->rex_r = (c & 7) >> 2;
71 | hs->rex_x = (c & 3) >> 1;
72 | hs->rex_b = c & 1;
73 | if (((c = *p++) & 0xf0) == 0x40) {
74 | opcode = c;
75 | goto error_opcode;
76 | }
77 | }
78 |
79 | if ((hs->opcode = c) == 0x0f) {
80 | hs->opcode2 = c = *p++;
81 | ht += DELTA_OPCODES;
82 | } else if (c >= 0xa0 && c <= 0xa3) {
83 | op64++;
84 | if (pref & PRE_67)
85 | pref |= PRE_66;
86 | else
87 | pref &= ~PRE_66;
88 | }
89 |
90 | opcode = c;
91 | cflags = ht[ht[opcode / 4] + (opcode % 4)];
92 |
93 | if (cflags == C_ERROR) {
94 | error_opcode:
95 | hs->flags |= F_ERROR | F_ERROR_OPCODE;
96 | cflags = 0;
97 | if ((opcode & -3) == 0x24)
98 | cflags++;
99 | }
100 |
101 | x = 0;
102 | if (cflags & C_GROUP) {
103 | uint16_t t;
104 | t = *(uint16_t *)(ht + (cflags & 0x7f));
105 | cflags = (uint8_t)t;
106 | x = (uint8_t)(t >> 8);
107 | }
108 |
109 | if (hs->opcode2) {
110 | ht = hde64_table + DELTA_PREFIXES;
111 | if (ht[ht[opcode / 4] + (opcode % 4)] & pref)
112 | hs->flags |= F_ERROR | F_ERROR_OPCODE;
113 | }
114 |
115 | if (cflags & C_MODRM) {
116 | hs->flags |= F_MODRM;
117 | hs->modrm = c = *p++;
118 | hs->modrm_mod = m_mod = c >> 6;
119 | hs->modrm_rm = m_rm = c & 7;
120 | hs->modrm_reg = m_reg = (c & 0x3f) >> 3;
121 |
122 | if (x && ((x << m_reg) & 0x80))
123 | hs->flags |= F_ERROR | F_ERROR_OPCODE;
124 |
125 | if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) {
126 | uint8_t t = opcode - 0xd9;
127 | if (m_mod == 3) {
128 | ht = hde64_table + DELTA_FPU_MODRM + t*8;
129 | t = ht[m_reg] << m_rm;
130 | } else {
131 | ht = hde64_table + DELTA_FPU_REG;
132 | t = ht[t] << m_reg;
133 | }
134 | if (t & 0x80)
135 | hs->flags |= F_ERROR | F_ERROR_OPCODE;
136 | }
137 |
138 | if (pref & PRE_LOCK) {
139 | if (m_mod == 3) {
140 | hs->flags |= F_ERROR | F_ERROR_LOCK;
141 | } else {
142 | uint8_t *table_end, op = opcode;
143 | if (hs->opcode2) {
144 | ht = hde64_table + DELTA_OP2_LOCK_OK;
145 | table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK;
146 | } else {
147 | ht = hde64_table + DELTA_OP_LOCK_OK;
148 | table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK;
149 | op &= -2;
150 | }
151 | for (; ht != table_end; ht++)
152 | if (*ht++ == op) {
153 | if (!((*ht << m_reg) & 0x80))
154 | goto no_lock_error;
155 | else
156 | break;
157 | }
158 | hs->flags |= F_ERROR | F_ERROR_LOCK;
159 | no_lock_error:
160 | ;
161 | }
162 | }
163 |
164 | if (hs->opcode2) {
165 | switch (opcode) {
166 | case 0x20: case 0x22:
167 | m_mod = 3;
168 | if (m_reg > 4 || m_reg == 1)
169 | goto error_operand;
170 | else
171 | goto no_error_operand;
172 | case 0x21: case 0x23:
173 | m_mod = 3;
174 | if (m_reg == 4 || m_reg == 5)
175 | goto error_operand;
176 | else
177 | goto no_error_operand;
178 | }
179 | } else {
180 | switch (opcode) {
181 | case 0x8c:
182 | if (m_reg > 5)
183 | goto error_operand;
184 | else
185 | goto no_error_operand;
186 | case 0x8e:
187 | if (m_reg == 1 || m_reg > 5)
188 | goto error_operand;
189 | else
190 | goto no_error_operand;
191 | }
192 | }
193 |
194 | if (m_mod == 3) {
195 | uint8_t *table_end;
196 | if (hs->opcode2) {
197 | ht = hde64_table + DELTA_OP2_ONLY_MEM;
198 | table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM;
199 | } else {
200 | ht = hde64_table + DELTA_OP_ONLY_MEM;
201 | table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM;
202 | }
203 | for (; ht != table_end; ht += 2)
204 | if (*ht++ == opcode) {
205 | if (*ht++ & pref && !((*ht << m_reg) & 0x80))
206 | goto error_operand;
207 | else
208 | break;
209 | }
210 | goto no_error_operand;
211 | } else if (hs->opcode2) {
212 | switch (opcode) {
213 | case 0x50: case 0xd7: case 0xf7:
214 | if (pref & (PRE_NONE | PRE_66))
215 | goto error_operand;
216 | break;
217 | case 0xd6:
218 | if (pref & (PRE_F2 | PRE_F3))
219 | goto error_operand;
220 | break;
221 | case 0xc5:
222 | goto error_operand;
223 | }
224 | goto no_error_operand;
225 | } else
226 | goto no_error_operand;
227 |
228 | error_operand:
229 | hs->flags |= F_ERROR | F_ERROR_OPERAND;
230 | no_error_operand:
231 |
232 | c = *p++;
233 | if (m_reg <= 1) {
234 | if (opcode == 0xf6)
235 | cflags |= C_IMM8;
236 | else if (opcode == 0xf7)
237 | cflags |= C_IMM_P66;
238 | }
239 |
240 | switch (m_mod) {
241 | case 0:
242 | if (pref & PRE_67) {
243 | if (m_rm == 6)
244 | disp_size = 2;
245 | } else
246 | if (m_rm == 5)
247 | disp_size = 4;
248 | break;
249 | case 1:
250 | disp_size = 1;
251 | break;
252 | case 2:
253 | disp_size = 2;
254 | if (!(pref & PRE_67))
255 | disp_size <<= 1;
256 | }
257 |
258 | if (m_mod != 3 && m_rm == 4) {
259 | hs->flags |= F_SIB;
260 | p++;
261 | hs->sib = c;
262 | hs->sib_scale = c >> 6;
263 | hs->sib_index = (c & 0x3f) >> 3;
264 | if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1))
265 | disp_size = 4;
266 | }
267 |
268 | p--;
269 | switch (disp_size) {
270 | case 1:
271 | hs->flags |= F_DISP8;
272 | hs->disp.disp8 = *p;
273 | break;
274 | case 2:
275 | hs->flags |= F_DISP16;
276 | hs->disp.disp16 = *(uint16_t *)p;
277 | break;
278 | case 4:
279 | hs->flags |= F_DISP32;
280 | hs->disp.disp32 = *(uint32_t *)p;
281 | }
282 | p += disp_size;
283 | } else if (pref & PRE_LOCK)
284 | hs->flags |= F_ERROR | F_ERROR_LOCK;
285 |
286 | if (cflags & C_IMM_P66) {
287 | if (cflags & C_REL32) {
288 | if (pref & PRE_66) {
289 | hs->flags |= F_IMM16 | F_RELATIVE;
290 | hs->imm.imm16 = *(uint16_t *)p;
291 | p += 2;
292 | goto disasm_done;
293 | }
294 | goto rel32_ok;
295 | }
296 | if (op64) {
297 | hs->flags |= F_IMM64;
298 | hs->imm.imm64 = *(uint64_t *)p;
299 | p += 8;
300 | } else if (!(pref & PRE_66)) {
301 | hs->flags |= F_IMM32;
302 | hs->imm.imm32 = *(uint32_t *)p;
303 | p += 4;
304 | } else
305 | goto imm16_ok;
306 | }
307 |
308 |
309 | if (cflags & C_IMM16) {
310 | imm16_ok:
311 | hs->flags |= F_IMM16;
312 | hs->imm.imm16 = *(uint16_t *)p;
313 | p += 2;
314 | }
315 | if (cflags & C_IMM8) {
316 | hs->flags |= F_IMM8;
317 | hs->imm.imm8 = *p++;
318 | }
319 |
320 | if (cflags & C_REL32) {
321 | rel32_ok:
322 | hs->flags |= F_IMM32 | F_RELATIVE;
323 | hs->imm.imm32 = *(uint32_t *)p;
324 | p += 4;
325 | } else if (cflags & C_REL8) {
326 | hs->flags |= F_IMM8 | F_RELATIVE;
327 | hs->imm.imm8 = *p++;
328 | }
329 |
330 | disasm_done:
331 |
332 | if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) {
333 | hs->flags |= F_ERROR | F_ERROR_LENGTH;
334 | hs->len = 15;
335 | }
336 |
337 | return (unsigned int)hs->len;
338 | }
339 |
340 | #endif // defined(_M_X64) || defined(__x86_64__)
341 |
--------------------------------------------------------------------------------
/mh/hde/hde64.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Hacker Disassembler Engine 64
3 | * Copyright (c) 2008-2009, Vyacheslav Patkov.
4 | * All rights reserved.
5 | *
6 | * hde64.h: C/C++ header file
7 | *
8 | */
9 |
10 | #ifndef _HDE64_H_
11 | #define _HDE64_H_
12 |
13 | /* stdint.h - C99 standard header
14 | * http://en.wikipedia.org/wiki/stdint.h
15 | *
16 | * if your compiler doesn't contain "stdint.h" header (for
17 | * example, Microsoft Visual C++), you can download file:
18 | * http://www.azillionmonkeys.com/qed/pstdint.h
19 | * and change next line to:
20 | * #include "pstdint.h"
21 | */
22 | #include "pstdint.h"
23 |
24 | #define F_MODRM 0x00000001
25 | #define F_SIB 0x00000002
26 | #define F_IMM8 0x00000004
27 | #define F_IMM16 0x00000008
28 | #define F_IMM32 0x00000010
29 | #define F_IMM64 0x00000020
30 | #define F_DISP8 0x00000040
31 | #define F_DISP16 0x00000080
32 | #define F_DISP32 0x00000100
33 | #define F_RELATIVE 0x00000200
34 | #define F_ERROR 0x00001000
35 | #define F_ERROR_OPCODE 0x00002000
36 | #define F_ERROR_LENGTH 0x00004000
37 | #define F_ERROR_LOCK 0x00008000
38 | #define F_ERROR_OPERAND 0x00010000
39 | #define F_PREFIX_REPNZ 0x01000000
40 | #define F_PREFIX_REPX 0x02000000
41 | #define F_PREFIX_REP 0x03000000
42 | #define F_PREFIX_66 0x04000000
43 | #define F_PREFIX_67 0x08000000
44 | #define F_PREFIX_LOCK 0x10000000
45 | #define F_PREFIX_SEG 0x20000000
46 | #define F_PREFIX_REX 0x40000000
47 | #define F_PREFIX_ANY 0x7f000000
48 |
49 | #define PREFIX_SEGMENT_CS 0x2e
50 | #define PREFIX_SEGMENT_SS 0x36
51 | #define PREFIX_SEGMENT_DS 0x3e
52 | #define PREFIX_SEGMENT_ES 0x26
53 | #define PREFIX_SEGMENT_FS 0x64
54 | #define PREFIX_SEGMENT_GS 0x65
55 | #define PREFIX_LOCK 0xf0
56 | #define PREFIX_REPNZ 0xf2
57 | #define PREFIX_REPX 0xf3
58 | #define PREFIX_OPERAND_SIZE 0x66
59 | #define PREFIX_ADDRESS_SIZE 0x67
60 |
61 | #pragma pack(push,1)
62 |
63 | typedef struct {
64 | uint8_t len;
65 | uint8_t p_rep;
66 | uint8_t p_lock;
67 | uint8_t p_seg;
68 | uint8_t p_66;
69 | uint8_t p_67;
70 | uint8_t rex;
71 | uint8_t rex_w;
72 | uint8_t rex_r;
73 | uint8_t rex_x;
74 | uint8_t rex_b;
75 | uint8_t opcode;
76 | uint8_t opcode2;
77 | uint8_t modrm;
78 | uint8_t modrm_mod;
79 | uint8_t modrm_reg;
80 | uint8_t modrm_rm;
81 | uint8_t sib;
82 | uint8_t sib_scale;
83 | uint8_t sib_index;
84 | uint8_t sib_base;
85 | union {
86 | uint8_t imm8;
87 | uint16_t imm16;
88 | uint32_t imm32;
89 | uint64_t imm64;
90 | } imm;
91 | union {
92 | uint8_t disp8;
93 | uint16_t disp16;
94 | uint32_t disp32;
95 | } disp;
96 | uint32_t flags;
97 | } hde64s;
98 |
99 | #pragma pack(pop)
100 |
101 | #ifdef __cplusplus
102 | extern "C" {
103 | #endif
104 |
105 | /* __cdecl */
106 | unsigned int hde64_disasm(const void *code, hde64s *hs);
107 |
108 | #ifdef __cplusplus
109 | }
110 | #endif
111 |
112 | #endif /* _HDE64_H_ */
113 |
--------------------------------------------------------------------------------
/mh/hde/pstdint.h:
--------------------------------------------------------------------------------
1 | /*
2 | * MinHook - The Minimalistic API Hooking Library for x64/x86
3 | * Copyright (C) 2009-2017 Tsuda Kageyu. All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions
7 | * are met:
8 | *
9 | * 1. Redistributions of source code must retain the above copyright
10 | * notice, this list of conditions and the following disclaimer.
11 | * 2. Redistributions in binary form must reproduce the above copyright
12 | * notice, this list of conditions and the following disclaimer in the
13 | * documentation and/or other materials provided with the distribution.
14 | *
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | */
26 |
27 | #pragma once
28 |
29 | #include
30 |
31 | // Integer types for HDE.
32 | typedef INT8 int8_t;
33 | typedef INT16 int16_t;
34 | typedef INT32 int32_t;
35 | typedef INT64 int64_t;
36 | typedef UINT8 uint8_t;
37 | typedef UINT16 uint16_t;
38 | typedef UINT32 uint32_t;
39 | typedef UINT64 uint64_t;
40 |
--------------------------------------------------------------------------------
/mh/hde/table32.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Hacker Disassembler Engine 32 C
3 | * Copyright (c) 2008-2009, Vyacheslav Patkov.
4 | * All rights reserved.
5 | *
6 | */
7 |
8 | #define C_NONE 0x00
9 | #define C_MODRM 0x01
10 | #define C_IMM8 0x02
11 | #define C_IMM16 0x04
12 | #define C_IMM_P66 0x10
13 | #define C_REL8 0x20
14 | #define C_REL32 0x40
15 | #define C_GROUP 0x80
16 | #define C_ERROR 0xff
17 |
18 | #define PRE_ANY 0x00
19 | #define PRE_NONE 0x01
20 | #define PRE_F2 0x02
21 | #define PRE_F3 0x04
22 | #define PRE_66 0x08
23 | #define PRE_67 0x10
24 | #define PRE_LOCK 0x20
25 | #define PRE_SEG 0x40
26 | #define PRE_ALL 0xff
27 |
28 | #define DELTA_OPCODES 0x4a
29 | #define DELTA_FPU_REG 0xf1
30 | #define DELTA_FPU_MODRM 0xf8
31 | #define DELTA_PREFIXES 0x130
32 | #define DELTA_OP_LOCK_OK 0x1a1
33 | #define DELTA_OP2_LOCK_OK 0x1b9
34 | #define DELTA_OP_ONLY_MEM 0x1cb
35 | #define DELTA_OP2_ONLY_MEM 0x1da
36 |
37 | unsigned char hde32_table[] = {
38 | 0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,
39 | 0xa8,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xac,0xaa,0xb2,0xaa,0x9f,0x9f,
40 | 0x9f,0x9f,0xb5,0xa3,0xa3,0xa4,0xaa,0xaa,0xba,0xaa,0x96,0xaa,0xa8,0xaa,0xc3,
41 | 0xc3,0x96,0x96,0xb7,0xae,0xd6,0xbd,0xa3,0xc5,0xa3,0xa3,0x9f,0xc3,0x9c,0xaa,
42 | 0xaa,0xac,0xaa,0xbf,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0x90,
43 | 0x82,0x7d,0x97,0x59,0x59,0x59,0x59,0x59,0x7f,0x59,0x59,0x60,0x7d,0x7f,0x7f,
44 | 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x9a,0x88,0x7d,
45 | 0x59,0x50,0x50,0x50,0x50,0x59,0x59,0x59,0x59,0x61,0x94,0x61,0x9e,0x59,0x59,
46 | 0x85,0x59,0x92,0xa3,0x60,0x60,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,
47 | 0x59,0x59,0x9f,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xcc,0x01,0xbc,0x03,0xf0,
48 | 0x10,0x10,0x10,0x10,0x50,0x50,0x50,0x50,0x14,0x20,0x20,0x20,0x20,0x01,0x01,
49 | 0x01,0x01,0xc4,0x02,0x10,0x00,0x00,0x00,0x00,0x01,0x01,0xc0,0xc2,0x10,0x11,
50 | 0x02,0x03,0x11,0x03,0x03,0x04,0x00,0x00,0x14,0x00,0x02,0x00,0x00,0xc6,0xc8,
51 | 0x02,0x02,0x02,0x02,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xca,
52 | 0x01,0x01,0x01,0x00,0x06,0x00,0x04,0x00,0xc0,0xc2,0x01,0x01,0x03,0x01,0xff,
53 | 0xff,0x01,0x00,0x03,0xc4,0xc4,0xc6,0x03,0x01,0x01,0x01,0xff,0x03,0x03,0x03,
54 | 0xc8,0x40,0x00,0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,
55 | 0x00,0x00,0x00,0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,
56 | 0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
57 | 0x00,0xff,0xff,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
58 | 0x7f,0x00,0x00,0xff,0x4a,0x4a,0x4a,0x4a,0x4b,0x52,0x4a,0x4a,0x4a,0x4a,0x4f,
59 | 0x4c,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x55,0x45,0x40,0x4a,0x4a,0x4a,
60 | 0x45,0x59,0x4d,0x46,0x4a,0x5d,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,
61 | 0x4a,0x4a,0x4a,0x4a,0x4a,0x61,0x63,0x67,0x4e,0x4a,0x4a,0x6b,0x6d,0x4a,0x4a,
62 | 0x45,0x6d,0x4a,0x4a,0x44,0x45,0x4a,0x4a,0x00,0x00,0x00,0x02,0x0d,0x06,0x06,
63 | 0x06,0x06,0x0e,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x00,0x06,0x06,0x02,0x06,
64 | 0x00,0x0a,0x0a,0x07,0x07,0x06,0x02,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04,
65 | 0x04,0x04,0x00,0x00,0x00,0x0e,0x05,0x06,0x06,0x06,0x01,0x06,0x00,0x00,0x08,
66 | 0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,
67 | 0x86,0x00,0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,
68 | 0xf8,0xbb,0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,
69 | 0xc4,0xff,0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,
70 | 0x13,0x09,0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,
71 | 0xb2,0xff,0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,
72 | 0xe7,0x08,0x00,0xf0,0x02,0x00
73 | };
74 |
--------------------------------------------------------------------------------
/mh/hde/table64.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Hacker Disassembler Engine 64 C
3 | * Copyright (c) 2008-2009, Vyacheslav Patkov.
4 | * All rights reserved.
5 | *
6 | */
7 |
8 | #define C_NONE 0x00
9 | #define C_MODRM 0x01
10 | #define C_IMM8 0x02
11 | #define C_IMM16 0x04
12 | #define C_IMM_P66 0x10
13 | #define C_REL8 0x20
14 | #define C_REL32 0x40
15 | #define C_GROUP 0x80
16 | #define C_ERROR 0xff
17 |
18 | #define PRE_ANY 0x00
19 | #define PRE_NONE 0x01
20 | #define PRE_F2 0x02
21 | #define PRE_F3 0x04
22 | #define PRE_66 0x08
23 | #define PRE_67 0x10
24 | #define PRE_LOCK 0x20
25 | #define PRE_SEG 0x40
26 | #define PRE_ALL 0xff
27 |
28 | #define DELTA_OPCODES 0x4a
29 | #define DELTA_FPU_REG 0xfd
30 | #define DELTA_FPU_MODRM 0x104
31 | #define DELTA_PREFIXES 0x13c
32 | #define DELTA_OP_LOCK_OK 0x1ae
33 | #define DELTA_OP2_LOCK_OK 0x1c6
34 | #define DELTA_OP_ONLY_MEM 0x1d8
35 | #define DELTA_OP2_ONLY_MEM 0x1e7
36 |
37 | unsigned char hde64_table[] = {
38 | 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5,
39 | 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1,
40 | 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea,
41 | 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0,
42 | 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab,
43 | 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92,
44 | 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90,
45 | 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b,
46 | 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,
47 | 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc,
48 | 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20,
49 | 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff,
50 | 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00,
51 | 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01,
52 | 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10,
53 | 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00,
54 | 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00,
55 | 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00,
56 | 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00,
57 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
58 | 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00,
59 | 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40,
60 | 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43,
61 | 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
62 | 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40,
63 | 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06,
64 | 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07,
65 | 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04,
66 | 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10,
67 | 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00,
68 | 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb,
69 | 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff,
70 | 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09,
71 | 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff,
72 | 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08,
73 | 0x00,0xf0,0x02,0x00
74 | };
75 |
--------------------------------------------------------------------------------
/mh/hook.c:
--------------------------------------------------------------------------------
1 | /*
2 | * MinHook - The Minimalistic API Hooking Library for x64/x86
3 | * Copyright (C) 2009-2017 Tsuda Kageyu.
4 | * All rights reserved.
5 | *
6 | * Redistribution and use in source and binary forms, with or without
7 | * modification, are permitted provided that the following conditions
8 | * are met:
9 | *
10 | * 1. Redistributions of source code must retain the above copyright
11 | * notice, this list of conditions and the following disclaimer.
12 | * 2. Redistributions in binary form must reproduce the above copyright
13 | * notice, this list of conditions and the following disclaimer in the
14 | * documentation and/or other materials provided with the distribution.
15 | *
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
20 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | #pragma warning(push)
30 | #pragma warning(disable:4244)
31 | #pragma warning(disable:26454)
32 | #pragma warning(disable:4310)
33 |
34 |
35 | #include
36 | #include
37 | #include
38 |
39 | #include "../mh/MinHook.h"
40 | #include "buffer.h"
41 | #include "trampoline.h"
42 |
43 | #ifndef ARRAYSIZE
44 | #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
45 | #endif
46 |
47 | // Initial capacity of the HOOK_ENTRY buffer.
48 | #define INITIAL_HOOK_CAPACITY 32
49 |
50 | // Initial capacity of the thread IDs buffer.
51 | #define INITIAL_THREAD_CAPACITY 128
52 |
53 | // Special hook position values.
54 | #define INVALID_HOOK_POS UINT_MAX
55 | #define ALL_HOOKS_POS UINT_MAX
56 |
57 | // Freeze() action argument defines.
58 | #define ACTION_DISABLE 0
59 | #define ACTION_ENABLE 1
60 | #define ACTION_APPLY_QUEUED 2
61 |
62 | // Thread access rights for suspending/resuming threads.
63 | #define THREAD_ACCESS \
64 | (THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SET_CONTEXT)
65 |
66 | // Hook information.
67 | typedef struct _HOOK_ENTRY
68 | {
69 | LPVOID pTarget; // Address of the target function.
70 | LPVOID pDetour; // Address of the detour or relay function.
71 | LPVOID pTrampoline; // Address of the trampoline function.
72 | UINT8 backup[8]; // Original prologue of the target function.
73 |
74 | UINT8 patchAbove : 1; // Uses the hot patch area.
75 | UINT8 isEnabled : 1; // Enabled.
76 | UINT8 queueEnable : 1; // Queued for enabling/disabling when != isEnabled.
77 |
78 | UINT nIP : 4; // Count of the instruction boundaries.
79 | UINT8 oldIPs[8]; // Instruction boundaries of the target function.
80 | UINT8 newIPs[8]; // Instruction boundaries of the trampoline function.
81 | } HOOK_ENTRY, *PHOOK_ENTRY;
82 |
83 | // Suspended threads for Freeze()/Unfreeze().
84 | typedef struct _FROZEN_THREADS
85 | {
86 | LPDWORD pItems; // Data heap
87 | UINT capacity; // Size of allocated data heap, items
88 | UINT size; // Actual number of data items
89 | } FROZEN_THREADS, *PFROZEN_THREADS;
90 |
91 | //-------------------------------------------------------------------------
92 | // Global Variables:
93 | //-------------------------------------------------------------------------
94 |
95 | // Spin lock flag for EnterSpinLock()/LeaveSpinLock().
96 | volatile LONG g_isLocked = FALSE;
97 |
98 | // Private heap handle. If not NULL, this library is initialized.
99 | HANDLE g_hHeap = NULL;
100 |
101 | // Hook entries.
102 | struct
103 | {
104 | PHOOK_ENTRY pItems; // Data heap
105 | UINT capacity; // Size of allocated data heap, items
106 | UINT size; // Actual number of data items
107 | } g_hooks;
108 |
109 | //-------------------------------------------------------------------------
110 | // Returns INVALID_HOOK_POS if not found.
111 | static UINT FindHookEntry(LPVOID pTarget)
112 | {
113 | UINT i;
114 | for (i = 0; i < g_hooks.size; ++i)
115 | {
116 | if ((ULONG_PTR)pTarget == (ULONG_PTR)g_hooks.pItems[i].pTarget)
117 | return i;
118 | }
119 |
120 | return INVALID_HOOK_POS;
121 | }
122 |
123 | //-------------------------------------------------------------------------
124 | static PHOOK_ENTRY AddHookEntry()
125 | {
126 | if (g_hooks.pItems == NULL)
127 | {
128 | g_hooks.capacity = INITIAL_HOOK_CAPACITY;
129 | g_hooks.pItems = (PHOOK_ENTRY)HeapAlloc(
130 | g_hHeap, 0, g_hooks.capacity * sizeof(HOOK_ENTRY));
131 | if (g_hooks.pItems == NULL)
132 | return NULL;
133 | }
134 | else if (g_hooks.size >= g_hooks.capacity)
135 | {
136 | PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc(
137 | g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity * 2) * sizeof(HOOK_ENTRY));
138 | if (p == NULL)
139 | return NULL;
140 |
141 | g_hooks.capacity *= 2;
142 | g_hooks.pItems = p;
143 | }
144 |
145 | return &g_hooks.pItems[g_hooks.size++];
146 | }
147 |
148 | //-------------------------------------------------------------------------
149 | static void DeleteHookEntry(UINT pos)
150 | {
151 | if (pos < g_hooks.size - 1)
152 | g_hooks.pItems[pos] = g_hooks.pItems[g_hooks.size - 1];
153 |
154 | g_hooks.size--;
155 |
156 | if (g_hooks.capacity / 2 >= INITIAL_HOOK_CAPACITY && g_hooks.capacity / 2 >= g_hooks.size)
157 | {
158 | PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc(
159 | g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity / 2) * sizeof(HOOK_ENTRY));
160 | if (p == NULL)
161 | return;
162 |
163 | g_hooks.capacity /= 2;
164 | g_hooks.pItems = p;
165 | }
166 | }
167 |
168 | //-------------------------------------------------------------------------
169 | static DWORD_PTR FindOldIP(PHOOK_ENTRY pHook, DWORD_PTR ip)
170 | {
171 | UINT i;
172 |
173 | if (pHook->patchAbove && ip == ((DWORD_PTR)pHook->pTarget - sizeof(JMP_REL)))
174 | return (DWORD_PTR)pHook->pTarget;
175 |
176 | for (i = 0; i < pHook->nIP; ++i)
177 | {
178 | if (ip == ((DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i]))
179 | return (DWORD_PTR)pHook->pTarget + pHook->oldIPs[i];
180 | }
181 |
182 | #if defined(_M_X64) || defined(__x86_64__)
183 | // Check relay function.
184 | if (ip == (DWORD_PTR)pHook->pDetour)
185 | return (DWORD_PTR)pHook->pTarget;
186 | #endif
187 |
188 | return 0;
189 | }
190 |
191 | //-------------------------------------------------------------------------
192 | static DWORD_PTR FindNewIP(PHOOK_ENTRY pHook, DWORD_PTR ip)
193 | {
194 | UINT i;
195 | for (i = 0; i < pHook->nIP; ++i)
196 | {
197 | if (ip == ((DWORD_PTR)pHook->pTarget + pHook->oldIPs[i]))
198 | return (DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i];
199 | }
200 |
201 | return 0;
202 | }
203 |
204 | //-------------------------------------------------------------------------
205 | static void ProcessThreadIPs(HANDLE hThread, UINT pos, UINT action)
206 | {
207 | // If the thread suspended in the overwritten area,
208 | // move IP to the proper address.
209 |
210 | CONTEXT c;
211 | #if defined(_M_X64) || defined(__x86_64__)
212 | DWORD64 *pIP = &c.Rip;
213 | #else
214 | DWORD *pIP = &c.Eip;
215 | #endif
216 | UINT count;
217 |
218 | c.ContextFlags = CONTEXT_CONTROL;
219 | if (!GetThreadContext(hThread, &c))
220 | return;
221 |
222 | if (pos == ALL_HOOKS_POS)
223 | {
224 | pos = 0;
225 | count = g_hooks.size;
226 | }
227 | else
228 | {
229 | count = pos + 1;
230 | }
231 |
232 | for (; pos < count; ++pos)
233 | {
234 | PHOOK_ENTRY pHook = &g_hooks.pItems[pos];
235 | BOOL enable;
236 | DWORD_PTR ip;
237 |
238 | switch (action)
239 | {
240 | case ACTION_DISABLE:
241 | enable = FALSE;
242 | break;
243 |
244 | case ACTION_ENABLE:
245 | enable = TRUE;
246 | break;
247 |
248 | default: // ACTION_APPLY_QUEUED
249 | enable = pHook->queueEnable;
250 | break;
251 | }
252 | if (pHook->isEnabled == enable)
253 | continue;
254 |
255 | if (enable)
256 | ip = FindNewIP(pHook, *pIP);
257 | else
258 | ip = FindOldIP(pHook, *pIP);
259 |
260 | if (ip != 0)
261 | {
262 | *pIP = ip;
263 | SetThreadContext(hThread, &c);
264 | }
265 | }
266 | }
267 |
268 | //-------------------------------------------------------------------------
269 | static VOID EnumerateThreads(PFROZEN_THREADS pThreads)
270 | {
271 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
272 | if (hSnapshot != INVALID_HANDLE_VALUE)
273 | {
274 | THREADENTRY32 te;
275 | te.dwSize = sizeof(THREADENTRY32);
276 | if (Thread32First(hSnapshot, &te))
277 | {
278 | do
279 | {
280 | if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(DWORD))
281 | && te.th32OwnerProcessID == GetCurrentProcessId()
282 | && te.th32ThreadID != GetCurrentThreadId())
283 | {
284 | if (pThreads->pItems == NULL)
285 | {
286 | pThreads->capacity = INITIAL_THREAD_CAPACITY;
287 | pThreads->pItems
288 | = (LPDWORD)HeapAlloc(g_hHeap, 0, pThreads->capacity * sizeof(DWORD));
289 | if (pThreads->pItems == NULL)
290 | break;
291 | }
292 | else if (pThreads->size >= pThreads->capacity)
293 | {
294 | LPDWORD p = (LPDWORD)HeapReAlloc(
295 | g_hHeap, 0, pThreads->pItems, (pThreads->capacity * 2) * sizeof(DWORD));
296 | if (p == NULL)
297 | break;
298 |
299 | pThreads->capacity *= 2;
300 | pThreads->pItems = p;
301 | }
302 | pThreads->pItems[pThreads->size++] = te.th32ThreadID;
303 | }
304 |
305 | te.dwSize = sizeof(THREADENTRY32);
306 | } while (Thread32Next(hSnapshot, &te));
307 | }
308 | CloseHandle(hSnapshot);
309 | }
310 | }
311 |
312 | //-------------------------------------------------------------------------
313 | static VOID Freeze(PFROZEN_THREADS pThreads, UINT pos, UINT action)
314 | {
315 | pThreads->pItems = NULL;
316 | pThreads->capacity = 0;
317 | pThreads->size = 0;
318 | EnumerateThreads(pThreads);
319 |
320 | if (pThreads->pItems != NULL)
321 | {
322 | UINT i;
323 | for (i = 0; i < pThreads->size; ++i)
324 | {
325 | HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]);
326 | if (hThread != NULL)
327 | {
328 | SuspendThread(hThread);
329 | ProcessThreadIPs(hThread, pos, action);
330 | CloseHandle(hThread);
331 | }
332 | }
333 | }
334 | }
335 |
336 | //-------------------------------------------------------------------------
337 | static VOID Unfreeze(PFROZEN_THREADS pThreads)
338 | {
339 | if (pThreads->pItems != NULL)
340 | {
341 | UINT i;
342 | for (i = 0; i < pThreads->size; ++i)
343 | {
344 | HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]);
345 | if (hThread != NULL)
346 | {
347 | ResumeThread(hThread);
348 | CloseHandle(hThread);
349 | }
350 | }
351 |
352 | HeapFree(g_hHeap, 0, pThreads->pItems);
353 | }
354 | }
355 |
356 | //-------------------------------------------------------------------------
357 | static MH_STATUS EnableHookLL(UINT pos, BOOL enable)
358 | {
359 | PHOOK_ENTRY pHook = &g_hooks.pItems[pos];
360 | DWORD oldProtect;
361 | SIZE_T patchSize = sizeof(JMP_REL);
362 | LPBYTE pPatchTarget = (LPBYTE)pHook->pTarget;
363 |
364 | if (pHook->patchAbove)
365 | {
366 | pPatchTarget -= sizeof(JMP_REL);
367 | patchSize += sizeof(JMP_REL_SHORT);
368 | }
369 |
370 | if (!VirtualProtect(pPatchTarget, patchSize, PAGE_EXECUTE_READWRITE, &oldProtect))
371 | return MH_ERROR_MEMORY_PROTECT;
372 |
373 | if (enable)
374 | {
375 | PJMP_REL pJmp = (PJMP_REL)pPatchTarget;
376 | pJmp->opcode = 0xE9;
377 | pJmp->operand = (UINT32)((LPBYTE)pHook->pDetour - (pPatchTarget + sizeof(JMP_REL)));
378 |
379 | if (pHook->patchAbove)
380 | {
381 | PJMP_REL_SHORT pShortJmp = (PJMP_REL_SHORT)pHook->pTarget;
382 | pShortJmp->opcode = 0xEB;
383 | pShortJmp->operand = (UINT8)(0 - (sizeof(JMP_REL_SHORT) + sizeof(JMP_REL)));
384 | }
385 | }
386 | else
387 | {
388 | if (pHook->patchAbove)
389 | memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL) + sizeof(JMP_REL_SHORT));
390 | else
391 | memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL));
392 | }
393 |
394 | VirtualProtect(pPatchTarget, patchSize, oldProtect, &oldProtect);
395 |
396 | // Just-in-case measure.
397 | FlushInstructionCache(GetCurrentProcess(), pPatchTarget, patchSize);
398 |
399 | pHook->isEnabled = enable;
400 | pHook->queueEnable = enable;
401 |
402 | return MH_OK;
403 | }
404 |
405 | //-------------------------------------------------------------------------
406 | static MH_STATUS EnableAllHooksLL(BOOL enable)
407 | {
408 | MH_STATUS status = MH_OK;
409 | UINT i, first = INVALID_HOOK_POS;
410 |
411 | for (i = 0; i < g_hooks.size; ++i)
412 | {
413 | if (g_hooks.pItems[i].isEnabled != enable)
414 | {
415 | first = i;
416 | break;
417 | }
418 | }
419 |
420 | if (first != INVALID_HOOK_POS)
421 | {
422 | FROZEN_THREADS threads;
423 | Freeze(&threads, ALL_HOOKS_POS, enable ? ACTION_ENABLE : ACTION_DISABLE);
424 |
425 | for (i = first; i < g_hooks.size; ++i)
426 | {
427 | if (g_hooks.pItems[i].isEnabled != enable)
428 | {
429 | status = EnableHookLL(i, enable);
430 | if (status != MH_OK)
431 | break;
432 | }
433 | }
434 |
435 | Unfreeze(&threads);
436 | }
437 |
438 | return status;
439 | }
440 |
441 | //-------------------------------------------------------------------------
442 | static VOID EnterSpinLock(VOID)
443 | {
444 | SIZE_T spinCount = 0;
445 |
446 | // Wait until the flag is FALSE.
447 | while (InterlockedCompareExchange(&g_isLocked, TRUE, FALSE) != FALSE)
448 | {
449 | // No need to generate a memory barrier here, since InterlockedCompareExchange()
450 | // generates a full memory barrier itself.
451 |
452 | // Prevent the loop from being too busy.
453 | if (spinCount < 32)
454 | Sleep(0);
455 | else
456 | Sleep(1);
457 |
458 | spinCount++;
459 | }
460 | }
461 |
462 | //-------------------------------------------------------------------------
463 | static VOID LeaveSpinLock(VOID)
464 | {
465 | // No need to generate a memory barrier here, since InterlockedExchange()
466 | // generates a full memory barrier itself.
467 |
468 | InterlockedExchange(&g_isLocked, FALSE);
469 | }
470 |
471 | //-------------------------------------------------------------------------
472 | MH_STATUS WINAPI MH_Initialize(VOID)
473 | {
474 | MH_STATUS status = MH_OK;
475 |
476 | EnterSpinLock();
477 |
478 | if (g_hHeap == NULL)
479 | {
480 | g_hHeap = HeapCreate(0, 0, 0);
481 | if (g_hHeap != NULL)
482 | {
483 | // Initialize the internal function buffer.
484 | InitializeBuffer();
485 | }
486 | else
487 | {
488 | status = MH_ERROR_MEMORY_ALLOC;
489 | }
490 | }
491 | else
492 | {
493 | status = MH_ERROR_ALREADY_INITIALIZED;
494 | }
495 |
496 | LeaveSpinLock();
497 |
498 | return status;
499 | }
500 |
501 | //-------------------------------------------------------------------------
502 | MH_STATUS WINAPI MH_Uninitialize(VOID)
503 | {
504 | MH_STATUS status = MH_OK;
505 |
506 | EnterSpinLock();
507 |
508 | if (g_hHeap != NULL)
509 | {
510 | status = EnableAllHooksLL(FALSE);
511 | if (status == MH_OK)
512 | {
513 | // Free the internal function buffer.
514 |
515 | // HeapFree is actually not required, but some tools detect a false
516 | // memory leak without HeapFree.
517 |
518 | UninitializeBuffer();
519 |
520 | HeapFree(g_hHeap, 0, g_hooks.pItems);
521 | HeapDestroy(g_hHeap);
522 |
523 | g_hHeap = NULL;
524 |
525 | g_hooks.pItems = NULL;
526 | g_hooks.capacity = 0;
527 | g_hooks.size = 0;
528 | }
529 | }
530 | else
531 | {
532 | status = MH_ERROR_NOT_INITIALIZED;
533 | }
534 |
535 | LeaveSpinLock();
536 |
537 | return status;
538 | }
539 |
540 | //-------------------------------------------------------------------------
541 | MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal)
542 | {
543 | MH_STATUS status = MH_OK;
544 |
545 | EnterSpinLock();
546 |
547 | if (g_hHeap != NULL)
548 | {
549 | if (IsExecutableAddress(pTarget) && IsExecutableAddress(pDetour))
550 | {
551 | UINT pos = FindHookEntry(pTarget);
552 | if (pos == INVALID_HOOK_POS)
553 | {
554 | LPVOID pBuffer = AllocateBuffer(pTarget);
555 | if (pBuffer != NULL)
556 | {
557 | TRAMPOLINE ct;
558 |
559 | ct.pTarget = pTarget;
560 | ct.pDetour = pDetour;
561 | ct.pTrampoline = pBuffer;
562 | if (CreateTrampolineFunction(&ct))
563 | {
564 | PHOOK_ENTRY pHook = AddHookEntry();
565 | if (pHook != NULL)
566 | {
567 | pHook->pTarget = ct.pTarget;
568 | #if defined(_M_X64) || defined(__x86_64__)
569 | pHook->pDetour = ct.pRelay;
570 | #else
571 | pHook->pDetour = ct.pDetour;
572 | #endif
573 | pHook->pTrampoline = ct.pTrampoline;
574 | pHook->patchAbove = ct.patchAbove;
575 | pHook->isEnabled = FALSE;
576 | pHook->queueEnable = FALSE;
577 | pHook->nIP = ct.nIP;
578 | memcpy(pHook->oldIPs, ct.oldIPs, ARRAYSIZE(ct.oldIPs));
579 | memcpy(pHook->newIPs, ct.newIPs, ARRAYSIZE(ct.newIPs));
580 |
581 | // Back up the target function.
582 |
583 | if (ct.patchAbove)
584 | {
585 | memcpy(
586 | pHook->backup,
587 | (LPBYTE)pTarget - sizeof(JMP_REL),
588 | sizeof(JMP_REL) + sizeof(JMP_REL_SHORT));
589 | }
590 | else
591 | {
592 | memcpy(pHook->backup, pTarget, sizeof(JMP_REL));
593 | }
594 |
595 | if (ppOriginal != NULL)
596 | *ppOriginal = pHook->pTrampoline;
597 | }
598 | else
599 | {
600 | status = MH_ERROR_MEMORY_ALLOC;
601 | }
602 | }
603 | else
604 | {
605 | status = MH_ERROR_UNSUPPORTED_FUNCTION;
606 | }
607 |
608 | if (status != MH_OK)
609 | {
610 | FreeBuffer(pBuffer);
611 | }
612 | }
613 | else
614 | {
615 | status = MH_ERROR_MEMORY_ALLOC;
616 | }
617 | }
618 | else
619 | {
620 | status = MH_ERROR_ALREADY_CREATED;
621 | }
622 | }
623 | else
624 | {
625 | status = MH_ERROR_NOT_EXECUTABLE;
626 | }
627 | }
628 | else
629 | {
630 | status = MH_ERROR_NOT_INITIALIZED;
631 | }
632 |
633 | LeaveSpinLock();
634 |
635 | return status;
636 | }
637 |
638 | //-------------------------------------------------------------------------
639 | MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget)
640 | {
641 | MH_STATUS status = MH_OK;
642 |
643 | EnterSpinLock();
644 |
645 | if (g_hHeap != NULL)
646 | {
647 | UINT pos = FindHookEntry(pTarget);
648 | if (pos != INVALID_HOOK_POS)
649 | {
650 | if (g_hooks.pItems[pos].isEnabled)
651 | {
652 | FROZEN_THREADS threads;
653 | Freeze(&threads, pos, ACTION_DISABLE);
654 |
655 | status = EnableHookLL(pos, FALSE);
656 |
657 | Unfreeze(&threads);
658 | }
659 |
660 | if (status == MH_OK)
661 | {
662 | FreeBuffer(g_hooks.pItems[pos].pTrampoline);
663 | DeleteHookEntry(pos);
664 | }
665 | }
666 | else
667 | {
668 | status = MH_ERROR_NOT_CREATED;
669 | }
670 | }
671 | else
672 | {
673 | status = MH_ERROR_NOT_INITIALIZED;
674 | }
675 |
676 | LeaveSpinLock();
677 |
678 | return status;
679 | }
680 |
681 | //-------------------------------------------------------------------------
682 | static MH_STATUS EnableHook(LPVOID pTarget, BOOL enable)
683 | {
684 | MH_STATUS status = MH_OK;
685 |
686 | EnterSpinLock();
687 |
688 | if (g_hHeap != NULL)
689 | {
690 | if (pTarget == MH_ALL_HOOKS)
691 | {
692 | status = EnableAllHooksLL(enable);
693 | }
694 | else
695 | {
696 | FROZEN_THREADS threads;
697 | UINT pos = FindHookEntry(pTarget);
698 | if (pos != INVALID_HOOK_POS)
699 | {
700 | if (g_hooks.pItems[pos].isEnabled != enable)
701 | {
702 | Freeze(&threads, pos, ACTION_ENABLE);
703 |
704 | status = EnableHookLL(pos, enable);
705 |
706 | Unfreeze(&threads);
707 | }
708 | else
709 | {
710 | status = enable ? MH_ERROR_ENABLED : MH_ERROR_DISABLED;
711 | }
712 | }
713 | else
714 | {
715 | status = MH_ERROR_NOT_CREATED;
716 | }
717 | }
718 | }
719 | else
720 | {
721 | status = MH_ERROR_NOT_INITIALIZED;
722 | }
723 |
724 | LeaveSpinLock();
725 |
726 | return status;
727 | }
728 |
729 | //-------------------------------------------------------------------------
730 | MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget)
731 | {
732 | return EnableHook(pTarget, TRUE);
733 | }
734 |
735 | //-------------------------------------------------------------------------
736 | MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget)
737 | {
738 | return EnableHook(pTarget, FALSE);
739 | }
740 |
741 | //-------------------------------------------------------------------------
742 | static MH_STATUS QueueHook(LPVOID pTarget, BOOL queueEnable)
743 | {
744 | MH_STATUS status = MH_OK;
745 |
746 | EnterSpinLock();
747 |
748 | if (g_hHeap != NULL)
749 | {
750 | if (pTarget == MH_ALL_HOOKS)
751 | {
752 | UINT i;
753 | for (i = 0; i < g_hooks.size; ++i)
754 | g_hooks.pItems[i].queueEnable = queueEnable;
755 | }
756 | else
757 | {
758 | UINT pos = FindHookEntry(pTarget);
759 | if (pos != INVALID_HOOK_POS)
760 | {
761 | g_hooks.pItems[pos].queueEnable = queueEnable;
762 | }
763 | else
764 | {
765 | status = MH_ERROR_NOT_CREATED;
766 | }
767 | }
768 | }
769 | else
770 | {
771 | status = MH_ERROR_NOT_INITIALIZED;
772 | }
773 |
774 | LeaveSpinLock();
775 |
776 | return status;
777 | }
778 |
779 | //-------------------------------------------------------------------------
780 | MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget)
781 | {
782 | return QueueHook(pTarget, TRUE);
783 | }
784 |
785 | //-------------------------------------------------------------------------
786 | MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget)
787 | {
788 | return QueueHook(pTarget, FALSE);
789 | }
790 |
791 | //-------------------------------------------------------------------------
792 | MH_STATUS WINAPI MH_ApplyQueued(VOID)
793 | {
794 | MH_STATUS status = MH_OK;
795 | UINT i, first = INVALID_HOOK_POS;
796 |
797 | EnterSpinLock();
798 |
799 | if (g_hHeap != NULL)
800 | {
801 | for (i = 0; i < g_hooks.size; ++i)
802 | {
803 | if (g_hooks.pItems[i].isEnabled != g_hooks.pItems[i].queueEnable)
804 | {
805 | first = i;
806 | break;
807 | }
808 | }
809 |
810 | if (first != INVALID_HOOK_POS)
811 | {
812 | FROZEN_THREADS threads;
813 | Freeze(&threads, ALL_HOOKS_POS, ACTION_APPLY_QUEUED);
814 |
815 | for (i = first; i < g_hooks.size; ++i)
816 | {
817 | PHOOK_ENTRY pHook = &g_hooks.pItems[i];
818 | if (pHook->isEnabled != pHook->queueEnable)
819 | {
820 | status = EnableHookLL(i, pHook->queueEnable);
821 | if (status != MH_OK)
822 | break;
823 | }
824 | }
825 |
826 | Unfreeze(&threads);
827 | }
828 | }
829 | else
830 | {
831 | status = MH_ERROR_NOT_INITIALIZED;
832 | }
833 |
834 | LeaveSpinLock();
835 |
836 | return status;
837 | }
838 |
839 | //-------------------------------------------------------------------------
840 | MH_STATUS WINAPI MH_CreateHookApiEx(
841 | LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour,
842 | LPVOID *ppOriginal, LPVOID *ppTarget)
843 | {
844 | HMODULE hModule;
845 | LPVOID pTarget;
846 |
847 | hModule = GetModuleHandleW(pszModule);
848 | if (hModule == NULL)
849 | return MH_ERROR_MODULE_NOT_FOUND;
850 |
851 | pTarget = (LPVOID)GetProcAddress(hModule, pszProcName);
852 | if (pTarget == NULL)
853 | return MH_ERROR_FUNCTION_NOT_FOUND;
854 |
855 | if(ppTarget != NULL)
856 | *ppTarget = pTarget;
857 |
858 | return MH_CreateHook(pTarget, pDetour, ppOriginal);
859 | }
860 |
861 | //-------------------------------------------------------------------------
862 | MH_STATUS WINAPI MH_CreateHookApi(
863 | LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal)
864 | {
865 | return MH_CreateHookApiEx(pszModule, pszProcName, pDetour, ppOriginal, NULL);
866 | }
867 |
868 | //-------------------------------------------------------------------------
869 | const char * WINAPI MH_StatusToString(MH_STATUS status)
870 | {
871 | #define MH_ST2STR(x) \
872 | case x: \
873 | return #x;
874 |
875 | switch (status) {
876 | MH_ST2STR(MH_UNKNOWN)
877 | MH_ST2STR(MH_OK)
878 | MH_ST2STR(MH_ERROR_ALREADY_INITIALIZED)
879 | MH_ST2STR(MH_ERROR_NOT_INITIALIZED)
880 | MH_ST2STR(MH_ERROR_ALREADY_CREATED)
881 | MH_ST2STR(MH_ERROR_NOT_CREATED)
882 | MH_ST2STR(MH_ERROR_ENABLED)
883 | MH_ST2STR(MH_ERROR_DISABLED)
884 | MH_ST2STR(MH_ERROR_NOT_EXECUTABLE)
885 | MH_ST2STR(MH_ERROR_UNSUPPORTED_FUNCTION)
886 | MH_ST2STR(MH_ERROR_MEMORY_ALLOC)
887 | MH_ST2STR(MH_ERROR_MEMORY_PROTECT)
888 | MH_ST2STR(MH_ERROR_MODULE_NOT_FOUND)
889 | MH_ST2STR(MH_ERROR_FUNCTION_NOT_FOUND)
890 | }
891 |
892 | #undef MH_ST2STR
893 |
894 | return "(unknown)";
895 | }
896 |
897 | #pragma warning(pop)
--------------------------------------------------------------------------------
/mh/trampoline.c:
--------------------------------------------------------------------------------
1 | /*
2 | * MinHook - The Minimalistic API Hooking Library for x64/x86
3 | * Copyright (C) 2009-2017 Tsuda Kageyu.
4 | * All rights reserved.
5 | *
6 | * Redistribution and use in source and binary forms, with or without
7 | * modification, are permitted provided that the following conditions
8 | * are met:
9 | *
10 | * 1. Redistributions of source code must retain the above copyright
11 | * notice, this list of conditions and the following disclaimer.
12 | * 2. Redistributions in binary form must reproduce the above copyright
13 | * notice, this list of conditions and the following disclaimer in the
14 | * documentation and/or other materials provided with the distribution.
15 | *
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
20 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | #pragma warning(push)
30 | #pragma warning(disable:4244)
31 |
32 |
33 | #include
34 |
35 | #ifndef ARRAYSIZE
36 | #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
37 | #endif
38 |
39 | #if defined(_M_X64) || defined(__x86_64__)
40 | #include "../mh/hde/hde64.h"
41 | typedef hde64s HDE;
42 | #define HDE_DISASM(code, hs) hde64_disasm(code, hs)
43 | #else
44 | #include "../mh/hde/hde32.h"
45 | typedef hde32s HDE;
46 | #define HDE_DISASM(code, hs) hde32_disasm(code, hs)
47 | #endif
48 |
49 | #include "trampoline.h"
50 | #include "buffer.h"
51 |
52 | // Maximum size of a trampoline function.
53 | #if defined(_M_X64) || defined(__x86_64__)
54 | #define TRAMPOLINE_MAX_SIZE (MEMORY_SLOT_SIZE - sizeof(JMP_ABS))
55 | #else
56 | #define TRAMPOLINE_MAX_SIZE MEMORY_SLOT_SIZE
57 | #endif
58 |
59 | //-------------------------------------------------------------------------
60 | static BOOL IsCodePadding(LPBYTE pInst, UINT size)
61 | {
62 | UINT i;
63 |
64 | if (pInst[0] != 0x00 && pInst[0] != 0x90 && pInst[0] != 0xCC)
65 | return FALSE;
66 |
67 | for (i = 1; i < size; ++i)
68 | {
69 | if (pInst[i] != pInst[0])
70 | return FALSE;
71 | }
72 | return TRUE;
73 | }
74 |
75 | //-------------------------------------------------------------------------
76 | BOOL CreateTrampolineFunction(PTRAMPOLINE ct)
77 | {
78 | #if defined(_M_X64) || defined(__x86_64__)
79 | CALL_ABS call = {
80 | 0xFF, 0x15, 0x00000002, // FF15 00000002: CALL [RIP+8]
81 | 0xEB, 0x08, // EB 08: JMP +10
82 | 0x0000000000000000ULL // Absolute destination address
83 | };
84 | JMP_ABS jmp = {
85 | 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6]
86 | 0x0000000000000000ULL // Absolute destination address
87 | };
88 | JCC_ABS jcc = {
89 | 0x70, 0x0E, // 7* 0E: J** +16
90 | 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6]
91 | 0x0000000000000000ULL // Absolute destination address
92 | };
93 | #else
94 | CALL_REL call = {
95 | 0xE8, // E8 xxxxxxxx: CALL +5+xxxxxxxx
96 | 0x00000000 // Relative destination address
97 | };
98 | JMP_REL jmp = {
99 | 0xE9, // E9 xxxxxxxx: JMP +5+xxxxxxxx
100 | 0x00000000 // Relative destination address
101 | };
102 | JCC_REL jcc = {
103 | 0x0F, 0x80, // 0F8* xxxxxxxx: J** +6+xxxxxxxx
104 | 0x00000000 // Relative destination address
105 | };
106 | #endif
107 |
108 | UINT8 oldPos = 0;
109 | UINT8 newPos = 0;
110 | ULONG_PTR jmpDest = 0; // Destination address of an internal jump.
111 | BOOL finished = FALSE; // Is the function completed?
112 | #if defined(_M_X64) || defined(__x86_64__)
113 | UINT8 instBuf[16];
114 | #endif
115 |
116 | ct->patchAbove = FALSE;
117 | ct->nIP = 0;
118 |
119 | do
120 | {
121 | HDE hs;
122 | UINT copySize;
123 | LPVOID pCopySrc;
124 | ULONG_PTR pOldInst = (ULONG_PTR)ct->pTarget + oldPos;
125 | ULONG_PTR pNewInst = (ULONG_PTR)ct->pTrampoline + newPos;
126 |
127 | copySize = HDE_DISASM((LPVOID)pOldInst, &hs);
128 | if (hs.flags & F_ERROR)
129 | return FALSE;
130 |
131 | pCopySrc = (LPVOID)pOldInst;
132 | if (oldPos >= sizeof(JMP_REL))
133 | {
134 | // The trampoline function is long enough.
135 | // Complete the function with the jump to the target function.
136 | #if defined(_M_X64) || defined(__x86_64__)
137 | jmp.address = pOldInst;
138 | #else
139 | jmp.operand = (UINT32)(pOldInst - (pNewInst + sizeof(jmp)));
140 | #endif
141 | pCopySrc = &jmp;
142 | copySize = sizeof(jmp);
143 |
144 | finished = TRUE;
145 | }
146 | #if defined(_M_X64) || defined(__x86_64__)
147 | else if ((hs.modrm & 0xC7) == 0x05)
148 | {
149 | // Instructions using RIP relative addressing. (ModR/M = 00???101B)
150 |
151 | // Modify the RIP relative address.
152 | PUINT32 pRelAddr;
153 |
154 | // Avoid using memcpy to reduce the footprint.
155 | #ifndef _MSC_VER
156 | memcpy(instBuf, (LPBYTE)pOldInst, copySize);
157 | #else
158 | __movsb(instBuf, (LPBYTE)pOldInst, copySize);
159 | #endif
160 | pCopySrc = instBuf;
161 |
162 | // Relative address is stored at (instruction length - immediate value length - 4).
163 | pRelAddr = (PUINT32)(instBuf + hs.len - ((hs.flags & 0x3C) >> 2) - 4);
164 | *pRelAddr
165 | = (UINT32)((pOldInst + hs.len + (INT32)hs.disp.disp32) - (pNewInst + hs.len));
166 |
167 | // Complete the function if JMP (FF /4).
168 | if (hs.opcode == 0xFF && hs.modrm_reg == 4)
169 | finished = TRUE;
170 | }
171 | #endif
172 | else if (hs.opcode == 0xE8)
173 | {
174 | // Direct relative CALL
175 | ULONG_PTR dest = pOldInst + hs.len + (INT32)hs.imm.imm32;
176 | #if defined(_M_X64) || defined(__x86_64__)
177 | call.address = dest;
178 | #else
179 | call.operand = (UINT32)(dest - (pNewInst + sizeof(call)));
180 | #endif
181 | pCopySrc = &call;
182 | copySize = sizeof(call);
183 | }
184 | else if ((hs.opcode & 0xFD) == 0xE9)
185 | {
186 | // Direct relative JMP (EB or E9)
187 | ULONG_PTR dest = pOldInst + hs.len;
188 |
189 | if (hs.opcode == 0xEB) // isShort jmp
190 | dest += (INT8)hs.imm.imm8;
191 | else
192 | dest += (INT32)hs.imm.imm32;
193 |
194 | // Simply copy an internal jump.
195 | if ((ULONG_PTR)ct->pTarget <= dest
196 | && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL)))
197 | {
198 | if (jmpDest < dest)
199 | jmpDest = dest;
200 | }
201 | else
202 | {
203 | #if defined(_M_X64) || defined(__x86_64__)
204 | jmp.address = dest;
205 | #else
206 | jmp.operand = (UINT32)(dest - (pNewInst + sizeof(jmp)));
207 | #endif
208 | pCopySrc = &jmp;
209 | copySize = sizeof(jmp);
210 |
211 | // Exit the function If it is not in the branch
212 | finished = (pOldInst >= jmpDest);
213 | }
214 | }
215 | else if ((hs.opcode & 0xF0) == 0x70
216 | || (hs.opcode & 0xFC) == 0xE0
217 | || (hs.opcode2 & 0xF0) == 0x80)
218 | {
219 | // Direct relative Jcc
220 | ULONG_PTR dest = pOldInst + hs.len;
221 |
222 | if ((hs.opcode & 0xF0) == 0x70 // Jcc
223 | || (hs.opcode & 0xFC) == 0xE0) // LOOPNZ/LOOPZ/LOOP/JECXZ
224 | dest += (INT8)hs.imm.imm8;
225 | else
226 | dest += (INT32)hs.imm.imm32;
227 |
228 | // Simply copy an internal jump.
229 | if ((ULONG_PTR)ct->pTarget <= dest
230 | && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL)))
231 | {
232 | if (jmpDest < dest)
233 | jmpDest = dest;
234 | }
235 | else if ((hs.opcode & 0xFC) == 0xE0)
236 | {
237 | // LOOPNZ/LOOPZ/LOOP/JCXZ/JECXZ to the outside are not supported.
238 | return FALSE;
239 | }
240 | else
241 | {
242 | UINT8 cond = ((hs.opcode != 0x0F ? hs.opcode : hs.opcode2) & 0x0F);
243 | #if defined(_M_X64) || defined(__x86_64__)
244 | // Invert the condition in x64 mode to simplify the conditional jump logic.
245 | jcc.opcode = 0x71 ^ cond;
246 | jcc.address = dest;
247 | #else
248 | jcc.opcode1 = 0x80 | cond;
249 | jcc.operand = (UINT32)(dest - (pNewInst + sizeof(jcc)));
250 | #endif
251 | pCopySrc = &jcc;
252 | copySize = sizeof(jcc);
253 | }
254 | }
255 | else if ((hs.opcode & 0xFE) == 0xC2)
256 | {
257 | // RET (C2 or C3)
258 |
259 | // Complete the function if not in a branch.
260 | finished = (pOldInst >= jmpDest);
261 | }
262 |
263 | // Can't alter the instruction length in a branch.
264 | if (pOldInst < jmpDest && copySize != hs.len)
265 | return FALSE;
266 |
267 | // Trampoline function is too large.
268 | if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE)
269 | return FALSE;
270 |
271 | // Trampoline function has too many instructions.
272 | if (ct->nIP >= ARRAYSIZE(ct->oldIPs))
273 | return FALSE;
274 |
275 | ct->oldIPs[ct->nIP] = oldPos;
276 | ct->newIPs[ct->nIP] = newPos;
277 | ct->nIP++;
278 |
279 | // Avoid using memcpy to reduce the footprint.
280 | #ifndef _MSC_VER
281 | memcpy((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize);
282 | #else
283 | __movsb((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize);
284 | #endif
285 | newPos += copySize;
286 | oldPos += hs.len;
287 | }
288 | while (!finished);
289 |
290 | // Is there enough place for a long jump?
291 | if (oldPos < sizeof(JMP_REL)
292 | && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL) - oldPos))
293 | {
294 | // Is there enough place for a short jump?
295 | if (oldPos < sizeof(JMP_REL_SHORT)
296 | && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL_SHORT) - oldPos))
297 | {
298 | return FALSE;
299 | }
300 |
301 | // Can we place the long jump above the function?
302 | if (!IsExecutableAddress((LPBYTE)ct->pTarget - sizeof(JMP_REL)))
303 | return FALSE;
304 |
305 | if (!IsCodePadding((LPBYTE)ct->pTarget - sizeof(JMP_REL), sizeof(JMP_REL)))
306 | return FALSE;
307 |
308 | ct->patchAbove = TRUE;
309 | }
310 |
311 | #if defined(_M_X64) || defined(__x86_64__)
312 | // Create a relay function.
313 | jmp.address = (ULONG_PTR)ct->pDetour;
314 |
315 | ct->pRelay = (LPBYTE)ct->pTrampoline + newPos;
316 | memcpy(ct->pRelay, &jmp, sizeof(jmp));
317 | #endif
318 |
319 | return TRUE;
320 | }
321 |
322 | #pragma warning(pop)
323 |
--------------------------------------------------------------------------------
/mh/trampoline.h:
--------------------------------------------------------------------------------
1 | /*
2 | * MinHook - The Minimalistic API Hooking Library for x64/x86
3 | * Copyright (C) 2009-2017 Tsuda Kageyu.
4 | * All rights reserved.
5 | *
6 | * Redistribution and use in source and binary forms, with or without
7 | * modification, are permitted provided that the following conditions
8 | * are met:
9 | *
10 | * 1. Redistributions of source code must retain the above copyright
11 | * notice, this list of conditions and the following disclaimer.
12 | * 2. Redistributions in binary form must reproduce the above copyright
13 | * notice, this list of conditions and the following disclaimer in the
14 | * documentation and/or other materials provided with the distribution.
15 | *
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
20 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | #pragma once
30 |
31 | #pragma pack(push, 1)
32 |
33 | // Structs for writing x86/x64 instructions.
34 |
35 | // 8-bit relative jump.
36 | typedef struct _JMP_REL_SHORT
37 | {
38 | UINT8 opcode; // EB xx: JMP +2+xx
39 | UINT8 operand;
40 | } JMP_REL_SHORT, *PJMP_REL_SHORT;
41 |
42 | // 32-bit direct relative jump/call.
43 | typedef struct _JMP_REL
44 | {
45 | UINT8 opcode; // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx
46 | UINT32 operand; // Relative destination address
47 | } JMP_REL, *PJMP_REL, CALL_REL;
48 |
49 | // 64-bit indirect absolute jump.
50 | typedef struct _JMP_ABS
51 | {
52 | UINT8 opcode0; // FF25 00000000: JMP [+6]
53 | UINT8 opcode1;
54 | UINT32 dummy;
55 | UINT64 address; // Absolute destination address
56 | } JMP_ABS, *PJMP_ABS;
57 |
58 | // 64-bit indirect absolute call.
59 | typedef struct _CALL_ABS
60 | {
61 | UINT8 opcode0; // FF15 00000002: CALL [+6]
62 | UINT8 opcode1;
63 | UINT32 dummy0;
64 | UINT8 dummy1; // EB 08: JMP +10
65 | UINT8 dummy2;
66 | UINT64 address; // Absolute destination address
67 | } CALL_ABS;
68 |
69 | // 32-bit direct relative conditional jumps.
70 | typedef struct _JCC_REL
71 | {
72 | UINT8 opcode0; // 0F8* xxxxxxxx: J** +6+xxxxxxxx
73 | UINT8 opcode1;
74 | UINT32 operand; // Relative destination address
75 | } JCC_REL;
76 |
77 | // 64bit indirect absolute conditional jumps that x64 lacks.
78 | typedef struct _JCC_ABS
79 | {
80 | UINT8 opcode; // 7* 0E: J** +16
81 | UINT8 dummy0;
82 | UINT8 dummy1; // FF25 00000000: JMP [+6]
83 | UINT8 dummy2;
84 | UINT32 dummy3;
85 | UINT64 address; // Absolute destination address
86 | } JCC_ABS;
87 |
88 | #pragma pack(pop)
89 |
90 | typedef struct _TRAMPOLINE
91 | {
92 | LPVOID pTarget; // [In] Address of the target function.
93 | LPVOID pDetour; // [In] Address of the detour function.
94 | LPVOID pTrampoline; // [In] Buffer address for the trampoline and relay function.
95 |
96 | #if defined(_M_X64) || defined(__x86_64__)
97 | LPVOID pRelay; // [Out] Address of the relay function.
98 | #endif
99 | BOOL patchAbove; // [Out] Should use the hot patch area?
100 | UINT nIP; // [Out] Number of the instruction boundaries.
101 | UINT8 oldIPs[8]; // [Out] Instruction boundaries of the target function.
102 | UINT8 newIPs[8]; // [Out] Instruction boundaries of the trampoline function.
103 | } TRAMPOLINE, *PTRAMPOLINE;
104 |
105 | BOOL CreateTrampolineFunction(PTRAMPOLINE ct);
106 |
--------------------------------------------------------------------------------
/pch.cpp:
--------------------------------------------------------------------------------
1 | // pch.cpp: source file corresponding to the pre-compiled header
2 |
3 | #include "pch.h"
4 |
5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
6 |
--------------------------------------------------------------------------------
/pch.h:
--------------------------------------------------------------------------------
1 | // pch.h: This is a precompiled header file.
2 | // Files listed below are compiled only once, improving build performance for future builds.
3 | // This also affects IntelliSense performance, including code completion and many code browsing features.
4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds.
5 | // Do not add files here that you will be updating frequently as this negates the performance advantage.
6 |
7 | #ifndef PCH_H
8 | #define PCH_H
9 |
10 | // add headers that you want to pre-compile here
11 | #include "framework.h"
12 |
13 | #endif //PCH_H
14 |
--------------------------------------------------------------------------------
/utils/engine.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kejjjjj/WaWMapExporter/dab1e62f7b4268a3ccd0368f174b22ff38c52239/utils/engine.cpp
--------------------------------------------------------------------------------
/utils/engine.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include
5 |
6 | template
7 | concept MemoryAddress_t = std::is_integral_v || std::is_pointer_v;
8 |
9 | namespace Engine
10 | {
11 | template
12 | constexpr inline Return call(const Mem address, Args... args)
13 | {
14 | return (reinterpret_cast(address))(args...);
15 | }
16 | }
17 |
18 | using namespace std::string_literals;
19 |
20 | namespace Engine::Tools
21 | {
22 |
23 | template
24 | void write_bytes(Mem dst, const std::string& bytes) {
25 | DWORD oldProtect = {};
26 | const auto size = bytes.length();
27 | VirtualProtect(reinterpret_cast(dst), size, PAGE_EXECUTE_READWRITE, &oldProtect);
28 | memcpy_s(reinterpret_cast(dst), size, bytes.c_str(), size);
29 | VirtualProtect(reinterpret_cast(dst), size, oldProtect, &oldProtect);
30 | }
31 |
32 | template
33 | void nop(Mem dst) {
34 | write_bytes(dst, "\x90\x90\x90\x90\x90"s);
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/utils/errors.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | #include "global_macros.hpp"
8 |
9 | #pragma warning(push)
10 | #pragma warning (disable : 4996)
11 |
12 | template
13 | inline void FatalError(const std::string& format, const Args&&... args)
14 | {
15 | char buffer[512];
16 | std::snprintf(buffer, sizeof(buffer), format.c_str(), args...);
17 | *(bool*)0xD5EC496 = true;
18 | strcpy((char*)0x1475ED0, buffer);
19 | ((void(*)())0x4FD030)();
20 |
21 | }
22 | template<>
23 | inline void FatalError(const std::string& format)
24 | {
25 |
26 |
27 | *(bool*)0xD5EC496 = true;
28 | strcpy((char*)0x1475ED0, format.c_str());
29 | ((void(*)())0x4FD030)();
30 |
31 | }
32 | #pragma warning(pop)
33 |
--------------------------------------------------------------------------------
/utils/functions.cpp:
--------------------------------------------------------------------------------
1 | #include "functions.hpp"
2 | #include
3 |
4 | float random(const float range) { //0 -> HI
5 | std::random_device rd;
6 | static std::mt19937 mt(rd());
7 | std::uniform_real_distribution num{ 0.f, range };
8 | return num(mt);
9 |
10 | }
11 | float random(const float min, const float range) { //LO -> HI
12 | std::random_device rd;
13 | static std::mt19937 mt(rd());
14 | std::uniform_real_distribution num{ min, range };
15 | return num(mt);
16 | }
--------------------------------------------------------------------------------
/utils/functions.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | template
7 | inline Return engine_call(const bool cod4x, const unsigned long offset, Args... args)
8 | {
9 | static HMODULE cod4x_handle = GetModuleHandle("cod4x_021.dll");
10 |
11 | if(!cod4x_handle)
12 | cod4x_handle = GetModuleHandle("cod4x_021.dll");
13 |
14 | return (reinterpret_cast(cod4x ? reinterpret_cast(cod4x_handle) + offset : offset))(args...);
15 | }
16 | inline std::string get_current_date() {
17 | SYSTEMTIME st;
18 | GetLocalTime(&st);
19 |
20 |
21 | return std::format("{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}",
22 | st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
23 | }
24 |
25 | static const std::string base64_chars =
26 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
27 | "abcdefghijklmnopqrstuvwxyz"
28 | "0123456789+/";
29 |
30 | inline bool is_base64(unsigned char c) {
31 | return (isalnum(c) || (c == '+') || (c == '/'));
32 | }
33 |
34 | inline std::string base64_decode(const std::string& encoded_string) {
35 | int in_len = encoded_string.size();
36 | int i = 0;
37 | int j = 0;
38 | int in_ = 0;
39 | unsigned char char_array_4[4]{}, char_array_3[3]{};
40 | std::string ret;
41 |
42 | while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
43 | char_array_4[i++] = encoded_string[in_]; in_++;
44 | if (i == 4) {
45 | for (i = 0; i < 4; i++)
46 | char_array_4[i] = static_cast(base64_chars.find(char_array_4[i]));
47 |
48 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
49 | char_array_3[1] = ((char_array_4[1] & 0x0F) << 4) + ((char_array_4[2] & 0x3C) >> 2);
50 | char_array_3[2] = ((char_array_4[2] & 0x03) << 6) + char_array_4[3];
51 |
52 | for (i = 0; (i < 3); i++)
53 | ret += char_array_3[i];
54 | i = 0;
55 | }
56 | }
57 |
58 | if (i) {
59 | for (j = i; j < 4; j++)
60 | char_array_4[j] = 0;
61 |
62 | for (j = 0; j < 4; j++)
63 | char_array_4[j] = static_cast(base64_chars.find(char_array_4[j]));
64 |
65 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
66 | char_array_3[1] = ((char_array_4[1] & 0x0F) << 4) + ((char_array_4[2] & 0x3C) >> 2);
67 |
68 | for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
69 | }
70 |
71 | return ret;
72 | }
73 | inline std::string base64_encode(const std::string& input) {
74 | std::string encoded_string;
75 | size_t len = input.size();
76 | size_t i = 0;
77 |
78 | while (i < len) {
79 | unsigned char char_array_3[3] = { 0 };
80 | unsigned char char_array_4[4] = { 0 };
81 |
82 | int j = 0;
83 | for (; j < 3 && (i + j) < len; ++j) {
84 | char_array_3[j] = input[i + j];
85 | }
86 |
87 | char_array_4[0] = (char_array_3[0] & 0xFC) >> 2;
88 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((j > 1) ? ((char_array_3[1] & 0xF0) >> 4) : 0);
89 | char_array_4[2] = ((j > 1) ? ((char_array_3[1] & 0x0F) << 2) + ((j > 2) ? ((char_array_3[2] & 0xC0) >> 6) : 0) : 0);
90 | char_array_4[3] = (j > 2) ? (char_array_3[2] & 0x3F) : 0;
91 |
92 | for (int k = 0; k < j + 1; ++k) {
93 | encoded_string += base64_chars[char_array_4[k]];
94 | }
95 |
96 | while (j++ < 3) {
97 | encoded_string += '=';
98 | }
99 |
100 | i += 3;
101 | }
102 |
103 | return encoded_string;
104 | }
105 |
106 |
107 | float random(const float range); //0 -> HI
108 | float random(const float min, const float range); //LO -> HI
--------------------------------------------------------------------------------
/utils/hook.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "mh/MinHook.h"
11 |
12 | //#include
13 |
14 | //template
15 | //using is_any_pointer = std::disjunction<
16 | // std::is_unsigned>,
17 | // std::is_pointer
18 | //>;
19 |
20 | #ifndef MINHOOK_LIBRARY
21 | #error "Minhook library is not in use"
22 | #endif
23 |
24 | struct hookbase
25 | {
26 | hookbase(const std::string& identifier, void* t, void* d) : target(t), detour(d), backup(t), s_id(identifier) {}
27 | virtual ~hookbase() = default;
28 |
29 | virtual bool create() = 0;
30 | virtual bool enable() = 0;
31 | virtual bool disable() = 0;
32 | virtual bool release() = 0;
33 |
34 | size_t get() const noexcept { return reinterpret_cast(target); }
35 | const std::string& id() const noexcept { return s_id; }
36 | void* get_jmp() const noexcept { return backup; }
37 | void* detour = {};
38 |
39 | protected:
40 |
41 | void* target = {};
42 | void* backup = {};
43 | std::string s_id;
44 | };
45 |
46 | template
47 | struct hook : public hookbase
48 | {
49 | hook(const std::string& identifier, void* t, void* d, bool enabled = true) : hookbase(identifier, t, d) {
50 | create();
51 |
52 | if (enabled)
53 | enable();
54 | }
55 | ~hook() = default;
56 |
57 | [[maybe_unused]] bool create() override { return MH_CreateHook(target, detour, &backup) == MH_OK; }
58 | [[maybe_unused]] bool enable() override { return MH_EnableHook(target) == MH_OK; }
59 | [[maybe_unused]] bool disable() override { return MH_DisableHook(target) == MH_OK; }
60 | [[maybe_unused]] bool release() override { return MH_RemoveHook(target) == MH_OK; }
61 |
62 | inline bool hook_owner() const noexcept(true) { return functions.empty(); }
63 | inline void exit() noexcept(true) { exit_hook = true; }
64 | inline void set_owner(hook* o) noexcept { owner = o; }
65 | inline auto get_owner() const { return owner; }
66 | inline auto& get_functions() { return functions; }
67 | decltype(auto) call(const Args... args) const
68 | {
69 | //std::cout << "calling: " << id() << " -> " << std::hex << backup << '\n';
70 | //call_queue(args...);
71 | return (reinterpret_cast(backup))(args...);
72 | }
73 | template
74 | void call(const Args... args) const
75 | {
76 | //call_queue(args...);
77 | //std::cout << "calling: " << id() << " -> " << std::hex << backup << '\n';
78 | (reinterpret_cast(backup))(args...);
79 | }
80 |
81 | decltype(auto) call_queueless(const Args... args) const { return (reinterpret_cast(backup))(args...); }
82 | template
83 | void call_queueless(const Args... args) const { (reinterpret_cast(backup))(args...); }
84 |
85 | decltype(auto) call_queue(const Args... args) const
86 | {
87 | std::for_each(functions.begin(), functions.end(), [&args...](const std::function& func)
88 | { func(args...); });
89 | }
90 | template
91 | void call_queue(const Args... args) const
92 | {
93 | std::for_each(functions.begin(), functions.end(), [&args...](const std::function& func)
94 | { func(args...); });
95 |
96 | }
97 |
98 | void add_callable(std::function&& _detour_) {
99 | functions.emplace_back(std::forward&&>(_detour_));
100 | }
101 |
102 | protected:
103 | hook* owner = {};
104 | std::vector> functions;
105 |
106 | bool exit_hook = false;
107 |
108 | };
109 | using phookbase = std::unique_ptr;
110 | struct hooktable
111 | {
112 | static bool initialize() { return MH_Initialize() == MH_OK; }
113 | static bool release() { return MH_Uninitialize() == MH_OK; }
114 |
115 | //overwrites any previous hooks if they exist
116 | template
117 | [[maybe_unused]] static hook* overwriter(const std::string& id, auto target, void* detour) {
118 | return dynamic_cast*>(
119 | table.emplace_back(std::make_unique>(id, reinterpret_cast(target), detour)).get());
120 | }
121 |
122 | //will not destroy any existing hook (any instance of this hook library)
123 | //will push this detour to the call queue at the end of the original detour
124 | template
125 | [[maybe_unused]] static hook* preserver(const std::string& id, auto target, void* detour) {
126 | return preserver(id, target, detour, reinterpret_cast*>(nullptr));
127 | }
128 |
129 | template
130 | [[maybe_unused]] static hook* preserver(const std::string& id, auto target, void* detour,
131 | hook* owner) {
132 |
133 | std::vector::iterator result;
134 | if ((result = std::find_if(table.begin(), table.end(), [&target](const phookbase& _hook_) {
135 | return _hook_->get() == size_t(target);
136 | })) == table.end()) {
137 | return overwriter(id, target, detour);
138 | }
139 |
140 | hook* current = dynamic_cast*>((result->get()));
141 |
142 | if (!owner || !current) {
143 | owner = current;
144 | if (!owner)
145 | return nullptr;
146 | }
147 |
148 | //auto last_target = tgt->get_functions().back().target();
149 | owner->add_callable(std::function(reinterpret_cast(detour)));
150 |
151 | //create a new hook inside of the detour
152 | hook* item = preserver(
153 | id,
154 | current->detour,
155 | reinterpret_cast(detour),
156 | owner);
157 |
158 | item->set_owner(owner);
159 | return item;
160 | }
161 |
162 | template
163 | static hook* find(const auto memory) {
164 |
165 | decltype(auto) _hook = std::find_if(table.begin(), table.end(), [&memory](const phookbase& _hook_)
166 | { return _hook_->get() == size_t(memory); });
167 |
168 | if (_hook == table.end())
169 | return nullptr;
170 |
171 | return dynamic_cast*>(_hook->get());
172 | }
173 | template
174 | static hook* find(const std::string& identifier) {
175 |
176 | decltype(auto) _hook = std::find_if(table.begin(), table.end(), [&identifier](const phookbase& _hook_)
177 | { return _hook_->id() == identifier; });
178 |
179 | if (_hook == table.end())
180 | return nullptr;
181 |
182 | return dynamic_cast*>(_hook->get());
183 | }
184 |
185 | protected:
186 | static std::vector table;
187 |
188 | };
189 | inline std::vector hooktable::table = {};
190 |
--------------------------------------------------------------------------------
/utils/resolution.cpp:
--------------------------------------------------------------------------------
1 | #include "resolution.hpp"
2 | #include "cg/cg_local.hpp"
3 | #include "cg/cg_offsets.hpp"
4 |
5 | ivec2 adjust_from_640x480(const fvec2 r)
6 | {
7 | return { adjust_from_640(r.x), adjust_from_480(r.y) };
8 | }
9 | fvec2 adjust_to_640x480(const ivec2 r)
10 | {
11 | return { adjust_to_640(r.x), adjust_to_480(r.y) };
12 | }
13 |
14 | int adjust_from_640(const float x)
15 | {
16 | auto real_w = 1280;
17 | auto scaleX = real_w / 640.f;
18 | return static_cast(x * scaleX);
19 |
20 | }
21 | int adjust_from_480(const float y)
22 | {
23 | auto real_h = 720;
24 | auto scaleY = real_h / 480.f;
25 | return static_cast(y * scaleY);
26 | }
27 | float adjust_to_640(const int x)
28 | {
29 | auto real_w = 1280;
30 | auto scaleX = 640.f / real_w;
31 | return (static_cast(x) * scaleX);
32 |
33 | }
34 | float adjust_to_480(const int y)
35 | {
36 | auto real_h = 720;
37 | auto scaleY = 480.f / real_h;
38 | return (static_cast(y) * scaleY);
39 | }
--------------------------------------------------------------------------------
/utils/resolution.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include "typedefs.hpp"
5 |
6 | template
7 | concept arithmetic = requires(T v) { std::is_arithmetic(); };
8 |
9 | template
10 | struct rect
11 | {
12 | T x = {};
13 | T y = {};
14 | T w = {};
15 | T h = {};
16 | };
17 |
18 | using irect = rect;
19 | using frect = rect;
20 | using drect = rect;
21 |
22 | ivec2 adjust_from_640x480(const fvec2 r);
23 | fvec2 adjust_to_640x480(const ivec2 r);
24 | int adjust_from_640(const float x);
25 | int adjust_from_480(const float y);
26 | float adjust_to_640(const int x);
27 | float adjust_to_480(const int y);
--------------------------------------------------------------------------------
/utils/typedefs.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | struct ImVec2;
8 |
9 | template
10 | struct vec3;
11 |
12 | template
13 | struct vec2
14 | {
15 | T x, y;
16 |
17 | constexpr vec2() { x = 0; y = 0; }
18 | constexpr vec2(const T& v) { x = v; y = v; }
19 | constexpr vec2(const T& a, const T& b) { x = a; y = b; }
20 | constexpr vec2(const T* a) { x = a[0]; y = a[1]; }
21 | constexpr vec2(const vec3& v);
22 |
23 | //constexpr vec2(const float(*a)[2]) { x = a[0]; y = a[1]; }
24 |
25 | constexpr vec2(const ImVec2& b);
26 | constexpr operator ImVec2();
27 | constexpr operator ImVec2() const;
28 |
29 | constexpr vec2 operator+(const vec2& v) const { return { x + v.x, y + v.y }; }
30 | constexpr vec2 operator-(const vec2& v) const { return { x - v.x, y - v.y }; }
31 | constexpr vec2 operator*(const vec2& v) const { return { x * v.x, y * v.y }; }
32 | constexpr vec2 operator/(const vec2& v) const { return { x / v.x, y / v.y }; }
33 | constexpr void operator=(const vec2& v) { x = v.x; y = v.y; }
34 | constexpr void operator=(const vec3& v);
35 |
36 | constexpr vec2 operator+(const T& v) const { return { x + v, y + v }; }
37 | constexpr vec2 operator-(const T& v) const { return { x - v, y - v }; }
38 | constexpr vec2 operator*(const T& v) const { return { x * v, y * v }; }
39 | constexpr vec2 operator/(const T& v) const { return { x / v, y / v }; }
40 |
41 | constexpr vec2 operator+=(const vec2& v) { return { x += v.x, y += v.y }; }
42 | constexpr vec2 operator-=(const vec2& v) { return { x -= v.x, y -= v.y }; }
43 | constexpr vec2 operator*=(const vec2& v) { return { x *= v.x, y *= v.y }; }
44 | constexpr vec2 operator/=(const vec2& v) { return { x /= v.x, y /= v.y }; }
45 | constexpr bool operator==(const vec2& v) const { return x == v.x && y == v.y; }
46 | constexpr bool operator!=(const vec2& v) const { return x != v.x || y != v.y; }
47 | constexpr bool operator>(const vec2& v) const { return x > v.x && y > v.y; }
48 | constexpr bool operator<(const vec2& v) const { return x < v.x && y < v.y; }
49 | constexpr bool operator>=(const vec2& v) const { return x >= v.x && y >= v.y; }
50 | constexpr bool operator<=(const vec2& v) const { return x <= v.x && y <= v.y; }
51 |
52 | constexpr vec2 operator+=(const T& v) { return { x += v, y += v }; }
53 | constexpr vec2 operator-=(const T& v) { return { x -= v, y -= v }; }
54 | constexpr vec2 operator*=(const T& v) { return { x *= v, y *= v }; }
55 | constexpr vec2 operator/=(const T& v) { return { x /= v, y /= v }; }
56 |
57 |
58 | constexpr T& operator[](const int index) const {
59 | return ((T*)&x)[index];
60 | }
61 | constexpr operator vec2() { return { (int)x, (int)y }; }
62 | constexpr operator vec2() { return { (float)x, (float)y }; }
63 |
64 |
65 | T mag() const {
66 | return std::sqrtf(x * x + y * y);
67 | }
68 | constexpr T dot(const vec2& vec) const
69 | {
70 | return x * vec.x + y * vec.y;
71 | }
72 | vec2 normalize() const noexcept {
73 | vec2 r = *this;
74 | float len = this->mag();
75 | float ilength;
76 |
77 | if (len) {
78 | ilength = 1 / len;
79 | r.x *= ilength;
80 | r.y *= ilength;
81 | }
82 |
83 | return r;
84 | }
85 | vec2 normalize(float& len) const noexcept {
86 | vec2 r = *this;
87 | len = this->mag();
88 | float ilength;
89 |
90 | if (len) {
91 | ilength = 1 / len;
92 | r.x *= ilength;
93 | r.y *= ilength;
94 | }
95 |
96 | return r;
97 | }
98 | T dist(const vec2& vec) const
99 | {
100 | const vec2 sub = *this - vec;
101 | return sub.mag();
102 | }
103 | constexpr vec2 inverse() const
104 | {
105 | return { -x, -y };
106 | }
107 | constexpr float MagSq() const { //magnitude squared
108 | return (x * x + y * y);
109 | }
110 | constexpr vec2 clamp(const T min, const T max) {
111 | vec2 r;
112 | if (x < min) r.x = min;
113 | else if (x > max) r.x = max;
114 |
115 | if (y < min) r.y = min;
116 | else if (y > max) r.y = max;
117 |
118 | return r;
119 | }
120 | constexpr vec2 normalize360() const noexcept
121 | {
122 | return vec2
123 | {
124 | (360.0f / 65536) * ((int)(x * (65536 / 360.0f)) & 65535),
125 | (360.0f / 65536) * ((int)(y * (65536 / 360.0f)) & 65535),
126 | };
127 | }
128 | constexpr vec2 normalize180() const noexcept
129 | {
130 | vec3 angle = normalize360();
131 | for (int i = 0; i < 2; i++) {
132 | if (angle[i] > 180.0f) {
133 | angle[i] -= 360.0f;
134 | }
135 | }
136 | return angle;
137 | }
138 | //expects radians
139 | vec2 fromAngle(T angle) {
140 | return { std::cosf(static_cast(angle)), std::cosf(static_cast(angle)) };
141 | }
142 | constexpr vec2 smooth(const vec2& dst, float smoothingFactor) const noexcept {
143 | smoothingFactor = std::max(0.f, std::min(1.f, smoothingFactor));
144 | return *this + smoothingFactor * (dst - *this);
145 | }
146 | constexpr vec2 angular_distance(const vec2& other)
147 | {
148 | constexpr const auto ce_fmodf = [](float x, float y) {
149 | return (y != 0.0f) ? x - static_cast(x / y) * y : 0.0f;
150 | };
151 |
152 | constexpr auto x_diff = ce_fmodf(other.x - x + 180, 360) - 180;
153 | constexpr auto y_diff = ce_fmodf(other.y - y + 180, 360) - 180;
154 |
155 | return vec2(x_diff, y_diff).normalize180();
156 | }
157 | template
158 | constexpr vec2 for_each(Func func, Args... args) const {
159 |
160 | return vec2
161 | {
162 | func(x, std::forward(args)...),
163 | func(y, std::forward(args)...),
164 | };
165 | }
166 | };
167 |
168 | using ivec2 = vec2;
169 | using fvec2 = vec2;
170 |
171 | template
172 | struct vec3
173 | {
174 |
175 | T x, y, z;
176 |
177 | constexpr vec3() { x = 0; y = 0, z = 0; }
178 | constexpr vec3(const T& v) { x = v; y = v, z = v; }
179 | constexpr vec3(const T& a, const T& b, const T& c) { x = a; y = b, z = c; }
180 | constexpr vec3(const T(*a)[3]) { x = a[0]; y = a[1], z = a[2]; }
181 | constexpr vec3(const T* a) { x = a[0]; y = a[1], z = a[2]; }
182 | constexpr vec3(const vec2& vec) { x = vec.x; y = vec.y; z = 0.f; }
183 | //constexpr explicit vec3(const vec3& v) { x = (int)v.x, y = (int)v.y; }
184 |
185 | constexpr vec3 operator+(const vec3& v) const { return { x + v.x, y + v.y, z + v.z }; }
186 | constexpr vec3 operator-(const vec3& v) const { return { x - v.x, y - v.y, z - v.z }; }
187 | constexpr vec3 operator*(const vec3& v) const { return { x * v.x, y * v.y, z * v.z }; }
188 | constexpr vec3 operator/(const vec3& v) const { return { x / v.x, y / v.y, z / v.z }; }
189 | constexpr void operator=(const vec3& v) { x = v.x; y = v.y, z = v.z; }
190 | constexpr void operator=(const T* v) { x = v[0]; y = v[1], z = v[2]; }
191 | std::basic_ostream> operator<< (const vec3& v) {};
192 |
193 | constexpr vec3 operator+(T v) const { return { x + v, y + v, z + v }; }
194 | constexpr vec3 operator-(T v) const { return { x - v, y - v, z - v }; }
195 | constexpr vec3 operator*(T v) const { return { x * v, y * v, z * v }; }
196 | constexpr vec3 operator/(T v) const { return { x / v, y / v, z / v }; }
197 |
198 | constexpr vec3 operator+=(const vec3& v) { return { x += v.x, y += v.y, z += v.z }; }
199 | constexpr vec3 operator-=(const vec3& v) { return { x -= v.x, y -= v.y, z -= v.z }; }
200 | constexpr vec3 operator*=(const vec3& v) { return { x *= v.x, y *= v.y, z *= v.z }; }
201 | constexpr vec3 operator/=(const vec3& v) { return { x /= v.x, y /= v.y, z /= v.z }; }
202 | constexpr bool operator==(const vec3& v) const { return x == v.x && y == v.y && z == v.z; }
203 | constexpr bool operator!=(const vec3& v) const { return x != v.x || y != v.y || z != v.z; }
204 | constexpr bool operator>(const vec3& v) const { return x > v.x && y > v.y && z > v.z; }
205 | constexpr bool operator<(const vec3& v) const { return x < v.x && y < v.y && z < v.z; }
206 | constexpr bool operator>=(const vec3& v) const { return x >= v.x && y >= v.y && z >= v.z; }
207 | constexpr bool operator<=(const vec3& v) const { return x <= v.x && y <= v.y && z <= v.z; }
208 |
209 | constexpr vec3 operator+=(T v) { return { x += v, y += v, z += v }; }
210 | constexpr vec3 operator-=(T v) { return { x -= v, y -= v, z -= v }; }
211 | constexpr vec3 operator*=(T v) { return { x *= v, y *= v, z *= v }; }
212 | constexpr vec3 operator/=(T v) { return { x /= v, y /= v, z /= v }; }
213 |
214 |
215 | #ifdef IMGUI_API
216 |
217 | #endif
218 | constexpr operator vec3() { return { (int)x, (int)y, (int)z }; }
219 | constexpr operator vec3() { return { (float)x, (float)y, (float)z }; }
220 | constexpr operator vec2() { return { (int)x, (int)y }; }
221 | constexpr operator vec2() { return { (float)x, (float)y }; }
222 | constexpr operator T* () { return &x; }
223 | //constexpr operator const T* () const { return &x; }
224 |
225 | constexpr T& operator[](const int index) const {
226 | return ((T*)&x)[index];
227 | }
228 |
229 | T mag() const noexcept {
230 | return static_cast(std::sqrtf(x * x + y * y + z * z));
231 | }
232 | constexpr T mag_sq() const noexcept {
233 | return (x * x + y * y + z * z);
234 | }
235 | constexpr T dot(const vec3& vec) const noexcept
236 | {
237 | return x * vec.x + y * vec.y + z * vec.z;
238 | }
239 | vec3 normalize() const noexcept {
240 | vec3 r = *this;
241 | const float length = this->mag();
242 | float ilength;
243 |
244 | if (length) {
245 | ilength = 1 / length;
246 | r.x *= ilength;
247 | r.y *= ilength;
248 | r.z *= ilength;
249 | }
250 |
251 | return r;
252 | }
253 | vec3 normalize(float& len) const noexcept {
254 | vec3 r = *this;
255 | len = this->mag();
256 | float ilength;
257 |
258 | if (len) {
259 | ilength = 1 / len;
260 | r.x *= ilength;
261 | r.y *= ilength;
262 | r.z *= ilength;
263 | }
264 |
265 | return r;
266 | }
267 | T dist(const vec3& vec) const noexcept
268 | {
269 | const vec3 sub = *this - vec;
270 | return sub.mag();
271 | }
272 | constexpr T dist_sq(const vec3& vec) const noexcept
273 | {
274 | const vec3 sub = *this - vec;
275 | return sub.mag_sq();
276 | }
277 | constexpr vec3 inverse() const noexcept
278 | {
279 | return { -x, -y, -z };
280 | }
281 | vec3 abs() const noexcept {
282 |
283 | constexpr auto cexpr_abs = [](const T v) {
284 | return v < static_cast(0) ? -v : v;
285 | };
286 |
287 | return { cexpr_abs(x), cexpr_abs(y), cexpr_abs(z) };
288 | }
289 | constexpr vec3 clamp(const T min, const T max) noexcept {
290 | vec3 r;
291 |
292 | if (x < min) r.x = min;
293 | else if (x > max) r.x = max;
294 |
295 | if (y < min) r.y = min;
296 | else if (y > max) r.y = max;
297 |
298 | if (z < min) r.z = min;
299 | else if (z > max) r.z = max;
300 |
301 | return r;
302 | }
303 | vec3 toangles() const noexcept
304 | {
305 | float forward;
306 | float yaw, pitch;
307 |
308 | if (y == 0 && x == 0) {
309 | yaw = 0;
310 | if (z > 0) {
311 | pitch = 90;
312 | }
313 | else {
314 | pitch = 270;
315 | }
316 | }
317 | else {
318 | if (x) {
319 | yaw = (std::atan2f(y, x) * 180 / 3.14159265358979323846f);
320 | }
321 | else if (y > 0) {
322 | yaw = 90;
323 | }
324 | else {
325 | yaw = 270;
326 | }
327 | //if (yaw < 0) {
328 | // yaw += 360;
329 | //}
330 |
331 | forward = sqrt(x * x + y * y);
332 | pitch = (atan2(z, forward) * 180 / 3.14159265358979323846f);
333 | if (pitch < 0) {
334 | pitch += 360;
335 | }
336 | }
337 |
338 | return vec3(-pitch, yaw, 0);
339 | }
340 | vec3 toforward() const noexcept {
341 | float angle;
342 | static float sp, sy, cp, cy;
343 | // static to help MS compiler fp bugs
344 |
345 | angle = y * (3.14159265358979323846f * 2 / 360);
346 | sy = sin(angle);
347 | cy = cos(angle);
348 |
349 | angle = x * (3.14159265358979323846f * 2 / 360);
350 | sp = sin(angle);
351 | cp = cos(angle);
352 |
353 | return vec3(cp * cy, cp * sy, -sp);
354 |
355 | }
356 | vec3 toright() const noexcept {
357 | float angle;
358 | static float sr, cr, sp, sy, cp, cy;
359 | // static to help MS compiler fp bugs
360 |
361 | angle = y * (3.14159265358979323846f * 2 / 360);
362 | sy = sin(angle);
363 | cy = cos(angle);
364 |
365 | angle = x * (3.14159265358979323846f * 2 / 360);
366 | sp = sin(angle);
367 | cp = cos(angle);
368 |
369 | angle = z * (3.14159265358979323846f * 2 / 360);
370 | sr = sin(angle);
371 | cr = cos(angle);
372 |
373 | return vec3((-1 * sr * sp * cy + -1 * cr * -sy), (-1 * sr * sp * sy + -1 * cr * cy), -1 * sr * cp);
374 |
375 | }
376 | vec3 toup() const noexcept {
377 | float angle;
378 | static float sr, cr, sp, sy, cp, cy;
379 | // static to help MS compiler fp bugs
380 |
381 | angle = y * (3.14159265358979323846f * 2 / 360);
382 | sy = sin(angle);
383 | cy = cos(angle);
384 |
385 | angle = x * (3.14159265358979323846f * 2 / 360);
386 | sp = sin(angle);
387 | cp = cos(angle);
388 |
389 | angle = z * (3.14159265358979323846f * 2 / 360);
390 | sr = sin(angle);
391 | cr = cos(angle);
392 |
393 | return vec3((cr * sp * cy + -sr * -sy), (cr * sp * sy + -sr * cy), cr * cp);
394 |
395 | }
396 | constexpr vec3 to_degrees()
397 | {
398 | constexpr float pi = 3.14159265358979323846f;
399 | return { x * (180.f / pi), y * (180.f / pi), z * (180.f / pi) };
400 | }
401 | constexpr vec3 normalize360() const noexcept
402 | {
403 | return vec3
404 | {
405 | (360.0f / 65536) * ((int)(x * (65536 / 360.0f)) & 65535),
406 | (360.0f / 65536) * ((int)(y * (65536 / 360.0f)) & 65535),
407 | (360.0f / 65536) * ((int)(z * (65536 / 360.0f)) & 65535),
408 |
409 | };
410 | }
411 | constexpr vec3 normalize180() const noexcept
412 | {
413 | vec3 angle = normalize360();
414 | for (int i = 0; i < 3; i++) {
415 | if (angle[i] > 180.0f) {
416 | angle[i] -= 360.0f;
417 | }
418 | }
419 | return angle;
420 | }
421 | constexpr vec3 angle_delta(const vec3& other) const noexcept
422 | {
423 | return (*this - other).normalize180();
424 | }
425 | constexpr vec3 smooth(const vec3& dst, float smoothingFactor) const noexcept {
426 | smoothingFactor = std::max(0.f, std::min(1.f, smoothingFactor));
427 | return *this + smoothingFactor * (dst - *this);
428 | }
429 | constexpr vec3 angular_distance(const vec3& other)
430 | {
431 | constexpr const auto ce_fmodf = [](float x, float y) {
432 | return (y != 0.0f) ? x - static_cast(x / y) * y : 0.0f;
433 | };
434 |
435 | constexpr auto x_diff = ce_fmodf(other.x - x + 180, 360) - 180;
436 | constexpr auto y_diff = ce_fmodf(other.y - y + 180, 360) - 180;
437 | constexpr auto z_diff = ce_fmodf(other.z - z + 180, 360) - 180;
438 |
439 | return vec3(x_diff, y_diff, z_diff).normalize180();
440 | }
441 |
442 | template
443 | constexpr vec3 for_each(Func func, Args... args) const {
444 |
445 | return vec3
446 | {
447 | func(x, std::forward(args)...),
448 | func(y, std::forward(args)...),
449 | func(z, std::forward(args)...)
450 | };
451 | }
452 | constexpr vec3 to_short() const noexcept
453 | {
454 | static_assert(std::is_floating_point_v);
455 | constexpr const auto a2s = [](float x) {
456 | return (static_cast((x) * 65536 / 360) & 65535);
457 | };
458 |
459 | return vec3{a2s(x), a2s(y), a2s(z) };
460 | }
461 | constexpr vec3 from_short() const noexcept
462 | {
463 | static_assert(std::is_integral_v