├── .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); 464 | constexpr const auto s2a = [](int x) { 465 | return static_cast((x) * (360.0f / 65536)); 466 | }; 467 | 468 | return vec3{s2a(x), s2a(y), s2a(z) }; 469 | } 470 | }; 471 | 472 | using ivec3 = vec3; 473 | using fvec3 = vec3; 474 | inline std::ostream& operator<<(std::ostream& os, fvec3 const& v) { 475 | return os << "{ " << v.x << ", " << v.y << ", " << v.z << " }"; 476 | } 477 | inline std::ostream& operator<<(std::ostream& os, ivec3 const& v) { 478 | return os << "{ " << v.x << ", " << v.y << ", " << v.z << " }"; 479 | } 480 | inline std::ostream& operator<<(std::ostream& os, fvec2 const& v) { 481 | return os << "{ " << v.x << ", " << v.y << " }"; 482 | } 483 | inline std::ostream& operator<<(std::ostream& os, ivec2 const& v) { 484 | return os << "{ " << v.x << ", " << v.y << " }"; 485 | } 486 | struct Quaternion { 487 | float w; 488 | float x; 489 | float y; 490 | float z; 491 | }; 492 | 493 | struct Pixel 494 | { 495 | uint8_t r, g, b, a; 496 | 497 | constexpr Pixel() { r = 0, g = 0, b = 0, a = 255; } 498 | constexpr Pixel(const uint8_t val) { r = val, g = val, b = val, a = val; }; 499 | constexpr Pixel(const uint8_t _r, const uint8_t _g, const uint8_t _b, const uint8_t _a) { r = _r, g = _g, b = _b, a = _a; } 500 | constexpr Pixel(const Pixel& p) { r = p.r, g = p.g, b = p.b, a = p.a; } 501 | 502 | Pixel operator=(const Pixel& px) { 503 | return { px.r, px.g, px.b, px.a }; 504 | } 505 | 506 | #ifdef IMGUI_API 507 | operator ImColor() { return ImColor(r, g, b, a); }; 508 | operator unsigned int() { return IM_COL32(r, g, b, a); }; 509 | 510 | #endif 511 | unsigned int packed() const { return *(unsigned int*)&r; } 512 | 513 | }; 514 | 515 | template 516 | constexpr vec2::vec2(const vec3& v) 517 | { 518 | x = v.x; 519 | y = v.y; 520 | } 521 | 522 | template 523 | constexpr void vec2::operator=(const vec3& v) 524 | { 525 | x = v.x; 526 | y = v.y; 527 | } 528 | 529 | --------------------------------------------------------------------------------