├── .gitattributes
├── .gitignore
├── MGS3CrouchWalk.sln
├── MGS3CrouchWalk
├── MGS3CrouchWalk.vcxproj
├── MGS3CrouchWalk.vcxproj.filters
├── Memory.cpp
├── Memory.h
├── MinHook.h
├── dllmain.cpp
├── framework.h
├── ini.h
└── types.h
├── README.md
└── libMinHook.lib
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.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/main/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 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # ASP.NET Scaffolding
66 | ScaffoldingReadMe.txt
67 |
68 | # StyleCop
69 | StyleCopReport.xml
70 |
71 | # Files built by Visual Studio
72 | *_i.c
73 | *_p.c
74 | *_h.h
75 | *.ilk
76 | *.meta
77 | *.obj
78 | *.iobj
79 | *.pch
80 | *.pdb
81 | *.ipdb
82 | *.pgc
83 | *.pgd
84 | *.rsp
85 | *.sbr
86 | *.tlb
87 | *.tli
88 | *.tlh
89 | *.tmp
90 | *.tmp_proj
91 | *_wpftmp.csproj
92 | *.log
93 | *.tlog
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 6 auto-generated project file (contains which files were open etc.)
298 | *.vbp
299 |
300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project)
301 | *.dsw
302 | *.dsp
303 |
304 | # Visual Studio 6 technical files
305 | *.ncb
306 | *.aps
307 |
308 | # Visual Studio LightSwitch build output
309 | **/*.HTMLClient/GeneratedArtifacts
310 | **/*.DesktopClient/GeneratedArtifacts
311 | **/*.DesktopClient/ModelManifest.xml
312 | **/*.Server/GeneratedArtifacts
313 | **/*.Server/ModelManifest.xml
314 | _Pvt_Extensions
315 |
316 | # Paket dependency manager
317 | .paket/paket.exe
318 | paket-files/
319 |
320 | # FAKE - F# Make
321 | .fake/
322 |
323 | # CodeRush personal settings
324 | .cr/personal
325 |
326 | # Python Tools for Visual Studio (PTVS)
327 | __pycache__/
328 | *.pyc
329 |
330 | # Cake - Uncomment if you are using it
331 | # tools/**
332 | # !tools/packages.config
333 |
334 | # Tabs Studio
335 | *.tss
336 |
337 | # Telerik's JustMock configuration file
338 | *.jmconfig
339 |
340 | # BizTalk build output
341 | *.btp.cs
342 | *.btm.cs
343 | *.odx.cs
344 | *.xsd.cs
345 |
346 | # OpenCover UI analysis results
347 | OpenCover/
348 |
349 | # Azure Stream Analytics local run output
350 | ASALocalRun/
351 |
352 | # MSBuild Binary and Structured Log
353 | *.binlog
354 |
355 | # NVidia Nsight GPU debugger configuration file
356 | *.nvuser
357 |
358 | # MFractors (Xamarin productivity tool) working folder
359 | .mfractor/
360 |
361 | # Local History for Visual Studio
362 | .localhistory/
363 |
364 | # Visual Studio History (VSHistory) files
365 | .vshistory/
366 |
367 | # BeatPulse healthcheck temp database
368 | healthchecksdb
369 |
370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
371 | MigrationBackup/
372 |
373 | # Ionide (cross platform F# VS Code tools) working folder
374 | .ionide/
375 |
376 | # Fody - auto-generated XML schema
377 | FodyWeavers.xsd
378 |
379 | # VS Code files for those working on multiple tools
380 | .vscode/*
381 | !.vscode/settings.json
382 | !.vscode/tasks.json
383 | !.vscode/launch.json
384 | !.vscode/extensions.json
385 | *.code-workspace
386 |
387 | # Local History for Visual Studio Code
388 | .history/
389 |
390 | # Windows Installer files from build outputs
391 | *.cab
392 | *.msi
393 | *.msix
394 | *.msm
395 | *.msp
396 |
397 | # JetBrains Rider
398 | *.sln.iml
399 |
--------------------------------------------------------------------------------
/MGS3CrouchWalk.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.7.34031.279
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MGS3CrouchWalk", "MGS3CrouchWalk\MGS3CrouchWalk.vcxproj", "{FE4A0B03-4DC5-4685-8D43-05F8C481C981}"
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 | {FE4A0B03-4DC5-4685-8D43-05F8C481C981}.Debug|x64.ActiveCfg = Debug|x64
17 | {FE4A0B03-4DC5-4685-8D43-05F8C481C981}.Debug|x64.Build.0 = Debug|x64
18 | {FE4A0B03-4DC5-4685-8D43-05F8C481C981}.Debug|x86.ActiveCfg = Debug|Win32
19 | {FE4A0B03-4DC5-4685-8D43-05F8C481C981}.Debug|x86.Build.0 = Debug|Win32
20 | {FE4A0B03-4DC5-4685-8D43-05F8C481C981}.Release|x64.ActiveCfg = Release|x64
21 | {FE4A0B03-4DC5-4685-8D43-05F8C481C981}.Release|x64.Build.0 = Release|x64
22 | {FE4A0B03-4DC5-4685-8D43-05F8C481C981}.Release|x86.ActiveCfg = Release|Win32
23 | {FE4A0B03-4DC5-4685-8D43-05F8C481C981}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {B125DC41-92C1-4083-A6F3-40781EC209D8}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/MGS3CrouchWalk/MGS3CrouchWalk.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 | {fe4a0b03-4dc5-4685-8d43-05f8c481c981}
25 | MGS3CrouchWalk
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 | Unicode
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 | .asi
75 |
76 |
77 | .asi
78 |
79 |
80 |
81 | Level3
82 | true
83 | WIN32;_DEBUG;MGS3CROUCHWALK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
84 | true
85 | Use
86 | pch.h
87 |
88 |
89 | Windows
90 | true
91 | false
92 |
93 |
94 |
95 |
96 | Level3
97 | true
98 | true
99 | true
100 | WIN32;NDEBUG;MGS3CROUCHWALK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
101 | true
102 | Use
103 | pch.h
104 |
105 |
106 | Windows
107 | true
108 | true
109 | true
110 | false
111 |
112 |
113 |
114 |
115 | Level3
116 | true
117 | _DEBUG;MGS3CROUCHWALK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
118 | true
119 | NotUsing
120 | pch.h
121 | stdcpplatest
122 |
123 |
124 | Windows
125 | true
126 | false
127 | $(CoreLibraryDependencies);%(AdditionalDependencies);$(SolutionDir)\libMinHook.lib;
128 |
129 |
130 |
131 |
132 | Level3
133 | true
134 | true
135 | true
136 | NDEBUG;MGS3CROUCHWALK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
137 | true
138 | NotUsing
139 | pch.h
140 | MaxSpeed
141 | false
142 | stdcpplatest
143 |
144 |
145 | Windows
146 | true
147 | true
148 | true
149 | false
150 | $(CoreLibraryDependencies);%(AdditionalDependencies);$(SolutionDir)\libMinHook.lib;Shlwapi.lib
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
--------------------------------------------------------------------------------
/MGS3CrouchWalk/MGS3CrouchWalk.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 |
18 |
19 | Header Files
20 |
21 |
22 | Header Files
23 |
24 |
25 | Header Files
26 |
27 |
28 | Header Files
29 |
30 |
31 | Header Files
32 |
33 |
34 |
35 |
36 | Source Files
37 |
38 |
39 | Source Files
40 |
41 |
42 |
--------------------------------------------------------------------------------
/MGS3CrouchWalk/Memory.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include "Memory.h"
7 | #include "MinHook.h"
8 |
9 | void Memory::DetourFunction(uint64_t target, LPVOID detour, LPVOID* ppOriginal)
10 | {
11 | int error = 0;
12 | if (error = MH_CreateHook((LPVOID)target, detour, ppOriginal) != 0)
13 | {
14 | return;
15 | }
16 | MH_EnableHook((LPVOID)target);
17 | }
18 |
19 | uintptr_t Memory::PatternScanBasic(uintptr_t beg, uintptr_t end, uint8_t* str, uintptr_t len)
20 | {
21 | for (uintptr_t ptr = beg; ptr < end - len; ++ptr)
22 | {
23 | if (0 == memcmp((const void*)ptr, str, len))
24 | return ptr;
25 | ptr++;
26 | }
27 |
28 | return 0;
29 | }
30 |
31 | // CSGOSimple's pattern scan
32 | // https://github.com/OneshotGH/CSGOSimple-master/blob/master/CSGOSimple/helpers/utils.cpp
33 | uint8_t* Memory::PatternScan(void* module, const char* signature)
34 | {
35 | static auto pattern_to_byte = [](const char* pattern) {
36 | auto bytes = std::vector{};
37 | auto start = const_cast(pattern);
38 | auto end = const_cast(pattern) + strlen(pattern);
39 |
40 | for (auto current = start; current < end; ++current) {
41 | if (*current == '?') {
42 | ++current;
43 | if (*current == '?')
44 | ++current;
45 | bytes.push_back(-1);
46 | }
47 | else {
48 | bytes.push_back(strtoul(current, ¤t, 16));
49 | }
50 | }
51 | return bytes;
52 | };
53 |
54 | auto dosHeader = (PIMAGE_DOS_HEADER)module;
55 | auto ntHeaders = (PIMAGE_NT_HEADERS)((std::uint8_t*)module + dosHeader->e_lfanew);
56 |
57 | auto sizeOfImage = ntHeaders->OptionalHeader.SizeOfImage;
58 | auto patternBytes = pattern_to_byte(signature);
59 | auto scanBytes = reinterpret_cast(module);
60 |
61 | auto s = patternBytes.size();
62 | auto d = patternBytes.data();
63 |
64 | for (auto i = 0ul; i < sizeOfImage - s; ++i) {
65 | bool found = true;
66 | for (auto j = 0ul; j < s; ++j) {
67 | if (scanBytes[i + j] != d[j] && d[j] != -1) {
68 | found = false;
69 | break;
70 | }
71 | }
72 | if (found) {
73 | return &scanBytes[i];
74 | }
75 | }
76 | return nullptr;
77 | }
--------------------------------------------------------------------------------
/MGS3CrouchWalk/Memory.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | namespace Memory
7 | {
8 | void DetourFunction(uint64_t target, LPVOID detour, LPVOID* ppOriginal);
9 | uintptr_t PatternScanBasic(uintptr_t beg, uintptr_t end, uint8_t* str, uintptr_t len);
10 | uint8_t* PatternScan(void* module, const char* signature);
11 | };
--------------------------------------------------------------------------------
/MGS3CrouchWalk/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 | #pragma once
30 |
31 | #if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__)
32 | #error MinHook supports only x86 and x64 systems.
33 | #endif
34 |
35 | #include
36 |
37 | // MinHook Error Codes.
38 | typedef enum MH_STATUS
39 | {
40 | // Unknown error. Should not be returned.
41 | MH_UNKNOWN = -1,
42 |
43 | // Successful.
44 | MH_OK = 0,
45 |
46 | // MinHook is already initialized.
47 | MH_ERROR_ALREADY_INITIALIZED,
48 |
49 | // MinHook is not initialized yet, or already uninitialized.
50 | MH_ERROR_NOT_INITIALIZED,
51 |
52 | // The hook for the specified target function is already created.
53 | MH_ERROR_ALREADY_CREATED,
54 |
55 | // The hook for the specified target function is not created yet.
56 | MH_ERROR_NOT_CREATED,
57 |
58 | // The hook for the specified target function is already enabled.
59 | MH_ERROR_ENABLED,
60 |
61 | // The hook for the specified target function is not enabled yet, or already
62 | // disabled.
63 | MH_ERROR_DISABLED,
64 |
65 | // The specified pointer is invalid. It points the address of non-allocated
66 | // and/or non-executable region.
67 | MH_ERROR_NOT_EXECUTABLE,
68 |
69 | // The specified target function cannot be hooked.
70 | MH_ERROR_UNSUPPORTED_FUNCTION,
71 |
72 | // Failed to allocate memory.
73 | MH_ERROR_MEMORY_ALLOC,
74 |
75 | // Failed to change the memory protection.
76 | MH_ERROR_MEMORY_PROTECT,
77 |
78 | // The specified module is not loaded.
79 | MH_ERROR_MODULE_NOT_FOUND,
80 |
81 | // The specified function is not found.
82 | MH_ERROR_FUNCTION_NOT_FOUND
83 | }
84 | MH_STATUS;
85 |
86 | // Can be passed as a parameter to MH_EnableHook, MH_DisableHook,
87 | // MH_QueueEnableHook or MH_QueueDisableHook.
88 | #define MH_ALL_HOOKS NULL
89 |
90 | #ifdef __cplusplus
91 | extern "C" {
92 | #endif
93 |
94 | // Initialize the MinHook library. You must call this function EXACTLY ONCE
95 | // at the beginning of your program.
96 | MH_STATUS WINAPI MH_Initialize(VOID);
97 |
98 | // Uninitialize the MinHook library. You must call this function EXACTLY
99 | // ONCE at the end of your program.
100 | MH_STATUS WINAPI MH_Uninitialize(VOID);
101 |
102 | // Creates a Hook for the specified target function, in disabled state.
103 | // Parameters:
104 | // pTarget [in] A pointer to the target function, which will be
105 | // overridden by the detour function.
106 | // pDetour [in] A pointer to the detour function, which will override
107 | // the target function.
108 | // ppOriginal [out] A pointer to the trampoline function, which will be
109 | // used to call the original target function.
110 | // This parameter can be NULL.
111 | MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID* ppOriginal);
112 |
113 | // Creates a Hook for the specified API function, in disabled state.
114 | // Parameters:
115 | // pszModule [in] A pointer to the loaded module name which contains the
116 | // target function.
117 | // pszTarget [in] A pointer to the target function name, which will be
118 | // overridden by the detour function.
119 | // pDetour [in] A pointer to the detour function, which will override
120 | // the target function.
121 | // ppOriginal [out] A pointer to the trampoline function, which will be
122 | // used to call the original target function.
123 | // This parameter can be NULL.
124 | MH_STATUS WINAPI MH_CreateHookApi(
125 | LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID* ppOriginal);
126 |
127 | // Creates a Hook for the specified API function, in disabled state.
128 | // Parameters:
129 | // pszModule [in] A pointer to the loaded module name which contains the
130 | // target function.
131 | // pszTarget [in] A pointer to the target function name, which will be
132 | // overridden by the detour function.
133 | // pDetour [in] A pointer to the detour function, which will override
134 | // the target function.
135 | // ppOriginal [out] A pointer to the trampoline function, which will be
136 | // used to call the original target function.
137 | // This parameter can be NULL.
138 | // ppTarget [out] A pointer to the target function, which will be used
139 | // with other functions.
140 | // This parameter can be NULL.
141 | MH_STATUS WINAPI MH_CreateHookApiEx(
142 | LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID* ppOriginal, LPVOID* ppTarget);
143 |
144 | // Removes an already created hook.
145 | // Parameters:
146 | // pTarget [in] A pointer to the target function.
147 | MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget);
148 |
149 | // Enables an already created hook.
150 | // Parameters:
151 | // pTarget [in] A pointer to the target function.
152 | // If this parameter is MH_ALL_HOOKS, all created hooks are
153 | // enabled in one go.
154 | MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget);
155 |
156 | // Disables an already created hook.
157 | // Parameters:
158 | // pTarget [in] A pointer to the target function.
159 | // If this parameter is MH_ALL_HOOKS, all created hooks are
160 | // disabled in one go.
161 | MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget);
162 |
163 | // Queues to enable an already created hook.
164 | // Parameters:
165 | // pTarget [in] A pointer to the target function.
166 | // If this parameter is MH_ALL_HOOKS, all created hooks are
167 | // queued to be enabled.
168 | MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget);
169 |
170 | // Queues to disable an already created hook.
171 | // Parameters:
172 | // pTarget [in] A pointer to the target function.
173 | // If this parameter is MH_ALL_HOOKS, all created hooks are
174 | // queued to be disabled.
175 | MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget);
176 |
177 | // Applies all queued changes in one go.
178 | MH_STATUS WINAPI MH_ApplyQueued(VOID);
179 |
180 | // Translates the MH_STATUS to its name as a string.
181 | const char* WINAPI MH_StatusToString(MH_STATUS status);
182 |
183 | #ifdef __cplusplus
184 | }
185 | #endif
186 |
187 |
--------------------------------------------------------------------------------
/MGS3CrouchWalk/dllmain.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "Memory.h"
3 | #include "MinHook.h"
4 | #include "ini.h"
5 | #include "types.h"
6 |
7 | HMODULE GameModule = GetModuleHandleA("METAL GEAR SOLID3.exe");
8 | uintptr_t GameBase = (uintptr_t)GameModule;
9 | uintptr_t* CamoIndexData = NULL;
10 | uintptr_t ActSquatStillOffset = 0;
11 | MovementWork* plWorkGlobal = NULL;
12 | MotionControl* mCtrlGlobal = NULL;
13 | mINI::INIStructure Config;
14 | bool CrouchWalkEnabled = false;
15 | bool CrouchMoving = false;
16 | bool CrouchMovingSlow = false;
17 | bool IgnoreButtonHold = false;
18 | bool HijackSequence = false;
19 |
20 | // config values
21 | float CamoIndexModifier = 1.0f;
22 | float CrouchWalkSpeed = 6.0f;
23 | float CrouchStalkSpeed = 3.0f;
24 | int CamoIndexValue = 0;
25 |
26 | InitializeCamoIndexDelegate* InitializeCamoIndex;
27 | CalculateCamoIndexDelegate* CalculateCamoIndex;
28 | ActionSquatStillDelegate* ActionSquatStill;
29 | PlayerSetMotionDelegate* PlayerSetMotionInternal;
30 | SetMotionDataDelegate* SetMotionData;
31 | PlayerStatusCheckDelegate* PlayerStatusCheck;
32 | PlayerStatusSetDelegate* PlayerStatusSet;
33 | ActMovementDelegate* ActMovement;
34 | GetButtonHoldingStateDelegate* GetButtonHoldingState;
35 | MotionPlaySequenceDelegate* MotionPlaySequence;
36 |
37 | uintptr_t PlayerSetMotion(int64_t work, PlayerMotion motion)
38 | {
39 | int motionIndex = (int)motion;
40 |
41 | if (motionIndex > 0)
42 | motionIndex--;
43 |
44 | return PlayerSetMotionInternal(work, (PlayerMotion)motionIndex);
45 | }
46 |
47 | int64_t __fastcall ActMovementHook(MovementWork* plWork, int64_t work, int flag)
48 | {
49 | if (plWorkGlobal != NULL && plWorkGlobal->action != ActSquatStillOffset)
50 | {
51 | CrouchWalkEnabled = false;
52 | CrouchMoving = false;
53 | }
54 |
55 | return ActMovement(plWork, work, flag);
56 | }
57 |
58 | void __fastcall SetMotionDataHook(MotionControl* motionControl, int layer, PlayerMotion motion, int time, int64_t mask)
59 | {
60 | if (motionControl->mtcmControl->mtarName == 0x6891CC)
61 | mCtrlGlobal = motionControl;
62 |
63 | if (motionControl == mCtrlGlobal && motion == PlayerMotion::StandMoveStalk && CrouchWalkEnabled)
64 | {
65 | float* currentTime = (float*)((uintptr_t)motionControl + 0x128);
66 |
67 | time = (int)*currentTime;
68 | motion = PlayerMotion::SquatMove;
69 |
70 | HijackSequence = true;
71 | }
72 |
73 | SetMotionData(motionControl, layer, motion, time, mask);
74 | HijackSequence = false;
75 | }
76 |
77 | int64_t __fastcall GetButtonHoldingStateHook(int64_t work, MovementWork* plWork)
78 | {
79 | if (IgnoreButtonHold)
80 | return 0;
81 |
82 | return GetButtonHoldingState(work, plWork);
83 | }
84 |
85 | int64_t __fastcall MotionPlaySequenceHook(__int64 mtsq_ctrl, int layer, int num, int flag, int loop_time)
86 | {
87 | if (HijackSequence)
88 | num = CrouchMovingSlow ? PlayerMotion::StandMoveSlow : PlayerMotion::StandMoveStalk;
89 |
90 | return MotionPlaySequence(mtsq_ctrl, layer, num, flag, loop_time);
91 | }
92 |
93 | int* __fastcall CalculateCamoIndexHook(int* a1, int a2)
94 | {
95 | int* result = CalculateCamoIndex(a1, a2);
96 |
97 | if (CamoIndexData == NULL || !CrouchWalkEnabled || !CrouchMoving)
98 | return result;
99 |
100 | int index = a2 << 7;
101 | auto camoIndex = (int*)((char*)&CamoIndexData[4] + index + 4);
102 |
103 | if (*camoIndex >= 1000) // ignore if stealth is equipped (todo: properly check item for ezgun and spider camo)
104 | return result;
105 |
106 | *camoIndex = *camoIndex < 0 ? *camoIndex / CamoIndexModifier : *camoIndex * CamoIndexModifier;
107 | *camoIndex += CamoIndexValue;
108 |
109 | if (*camoIndex > 950) *camoIndex = 950;
110 | if (*camoIndex < -1000) *camoIndex = -1000;
111 |
112 | return result;
113 | }
114 |
115 | int* __fastcall ActionSquatStillHook(int64_t work, MovementWork* plWork, int64_t a3, int64_t a4)
116 | {
117 | // we store this here so we don't have to hardcode another address that
118 | // needs to be updated with each new game patch
119 | plWorkGlobal = plWork;
120 |
121 | // process the default squatting logic while ignoring the button hold state check
122 | IgnoreButtonHold = true;
123 | int* result = ActionSquatStill(work, plWork, a3, a4);
124 | IgnoreButtonHold = false;
125 |
126 | // detect holding X while crouched to go into prone
127 | if (!PlayerStatusCheck(0xE0u))
128 | {
129 | auto buttonState = GetButtonHoldingState(work, plWork);
130 |
131 | if (buttonState == 1)
132 | plWork->flag |= MovementFlag::FlagStand;
133 | else if (buttonState == 2)
134 | plWork->flag |= MovementFlag::FlagSquatToGround;
135 | }
136 |
137 | // check that the pad is being held down
138 | int16_t padForce = *(int16_t*)(work + 2016);
139 | bool wasMoving = CrouchMoving;
140 | bool wasSlow = CrouchMovingSlow;
141 | CrouchMoving = padForce > plWork->padForceLimit;
142 | CrouchMovingSlow = padForce < 180;
143 |
144 | if (CrouchMoving && !PlayerStatusCheck(0xDE)) // 0xDE seems to make sure we aren't in first person mode
145 | {
146 | plWork->motion = PlayerSetMotion(work, CrouchWalkEnabled ? PlayerMotion::StandMoveStalk : PlayerMotion::RunUpwards);
147 |
148 | if (mCtrlGlobal != NULL)
149 | {
150 | mCtrlGlobal->mtcmControl->motionTimeBase = CrouchMovingSlow ? CrouchStalkSpeed : CrouchWalkSpeed;
151 |
152 | auto mtsqCntrl = *((uintptr_t*)mCtrlGlobal + 15);
153 |
154 | if (wasMoving && wasSlow != CrouchMovingSlow)
155 | {
156 | MotionPlaySequence(mtsqCntrl, 0, CrouchMovingSlow ? PlayerMotion::StandMoveSlow : PlayerMotion::StandMoveStalk, 5, 0x1b0);
157 | }
158 | }
159 |
160 | PlayerStatusSet(11, 14, 0x10C, 0xFFFFFFFF); // enables grass movement sounds
161 |
162 | CrouchWalkEnabled = true;
163 | }
164 |
165 | if (CrouchWalkEnabled && (plWork->flag & MovementFlag::FlagStand) != 0)
166 | CrouchWalkEnabled = false;
167 |
168 | return result;
169 | }
170 |
171 | void InstallHooks()
172 | {
173 | int status = MH_Initialize();
174 |
175 | uintptr_t actMovementOffset = (uintptr_t)Memory::PatternScan(GameModule, "40 53 56 57 48 81 EC F0 00 00 00 48 8B 05 ?? ?? ?? 00 48 33 C4 48 89 84 24 B0 00 00 00 48 8B F9");
176 | uintptr_t setMotionDataOffset = (uintptr_t)Memory::PatternScan(GameModule, "48 85 C9 0F 84 42 03 00 00 4C 8B DC 55 53 56 41");
177 | uintptr_t calcuateCamoIndexOffset = (uintptr_t)Memory::PatternScan(GameModule, "48 83 EC 30 0F 29 74 24 20 48 8B F9 48 63 F2 E8") - 0x10;
178 | uintptr_t getBtnHoldStateOffset = (uintptr_t)Memory::PatternScan(GameModule, "44 0F B7 8A 8E 00 00 00 4C 8B C2 66 45 85 C9 78");
179 | uintptr_t motionPlaySeqOffset = (uintptr_t)Memory::PatternScan(GameModule, "4D 63 D8 48 85 C9 74 6B 48 63 C2 48 8D 14 40 48");
180 | uint8_t* disableCrouchProneOffset = Memory::PatternScan(GameModule, "00 00 7E 19 83 4F 68 10");
181 |
182 | ActSquatStillOffset = (uintptr_t)Memory::PatternScan(GameModule, "4C 8B DC 55 57 41 56 49 8D 6B A1 48 81 EC 00 01");
183 | InitializeCamoIndex = (InitializeCamoIndexDelegate*)Memory::PatternScan(GameModule, "85 D2 75 33 0F 57 C0 48 63 C2 48 C1 E0 07 48 8D");
184 | PlayerSetMotionInternal = (PlayerSetMotionDelegate*)(Memory::PatternScan(GameModule, "B9 0F 01 00 00 E8 ?? 36 FF FF 85 C0 74 2A BA FF") - 0x10);
185 | PlayerStatusCheck = (PlayerStatusCheckDelegate*)Memory::PatternScan(GameModule, "8B D1 B8 01 00 00 00 83 E1 1F D3 E0 8B CA 48 C1");
186 | PlayerStatusSet = (PlayerStatusSetDelegate*)(Memory::PatternScan(GameModule, "04 89 0F AB D0 41 89 04") - 0x46);
187 |
188 | Memory::DetourFunction(actMovementOffset, (LPVOID)ActMovementHook, (LPVOID*)&ActMovement);
189 | Memory::DetourFunction(setMotionDataOffset, (LPVOID)SetMotionDataHook, (LPVOID*)&SetMotionData);
190 | Memory::DetourFunction(calcuateCamoIndexOffset, (LPVOID)CalculateCamoIndexHook, (LPVOID*)&CalculateCamoIndex);
191 | Memory::DetourFunction(getBtnHoldStateOffset, (LPVOID)GetButtonHoldingStateHook, (LPVOID*)&GetButtonHoldingState);
192 | Memory::DetourFunction(motionPlaySeqOffset, (LPVOID)MotionPlaySequenceHook, (LPVOID*)&MotionPlaySequence);
193 | Memory::DetourFunction(ActSquatStillOffset, (LPVOID)ActionSquatStillHook, (LPVOID*)&ActionSquatStill);
194 |
195 | CamoIndexData = InitializeCamoIndex(0, 0);
196 |
197 | DWORD oldProtect;
198 | VirtualProtect((LPVOID)disableCrouchProneOffset, 8, PAGE_EXECUTE_READWRITE, &oldProtect);
199 | disableCrouchProneOffset[7] = 0x00;
200 | }
201 |
202 | void ReadConfig()
203 | {
204 | mINI::INIFile file("MGS3CrouchWalk.ini");
205 | file.read(Config);
206 | CamoIndexModifier = std::stof(Config["Settings"]["CamoIndexModifier"]);
207 | CamoIndexValue = std::stoi(Config["Settings"]["CamoIndexValue"]) * 10;
208 | CrouchWalkSpeed = std::stof(Config["Settings"]["CrouchWalkSpeed"]);
209 | CrouchStalkSpeed = std::stof(Config["Settings"]["CrouchStalkSpeed"]);
210 | }
211 |
212 | DWORD WINAPI MainThread(LPVOID lpParam)
213 | {
214 | WCHAR exePath[_MAX_PATH] = { 0 };
215 | GetModuleFileName(GameModule, exePath, MAX_PATH);
216 | WCHAR* filename = PathFindFileName(exePath);
217 |
218 | if (wcsncmp(filename, L"launcher.exe", 13) == 0)
219 | return true;
220 |
221 | Sleep(3000); // delay, just in case
222 | ReadConfig();
223 | InstallHooks();
224 |
225 | return true;
226 | }
227 |
228 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
229 | {
230 | switch (ul_reason_for_call)
231 | {
232 | case DLL_PROCESS_ATTACH:
233 | DisableThreadLibraryCalls(hModule);
234 | CreateThread(NULL, NULL, MainThread, NULL, NULL, NULL);
235 | break;
236 | case DLL_THREAD_ATTACH:
237 | case DLL_THREAD_DETACH:
238 | case DLL_PROCESS_DETACH:
239 | break;
240 | }
241 | return TRUE;
242 | }
243 |
244 |
--------------------------------------------------------------------------------
/MGS3CrouchWalk/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 |
--------------------------------------------------------------------------------
/MGS3CrouchWalk/ini.h:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | * Copyright (c) 2018 Danijel Durakovic
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | * this software and associated documentation files (the "Software"), to deal in
7 | * the Software without restriction, including without limitation the rights to
8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 | * of the Software, and to permit persons to whom the Software is furnished to do
10 | * so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in all
13 | * copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 | *
22 | */
23 |
24 | ///////////////////////////////////////////////////////////////////////////////
25 | //
26 | // /mINI/ v0.9.14
27 | // An INI file reader and writer for the modern age.
28 | //
29 | ///////////////////////////////////////////////////////////////////////////////
30 | //
31 | // A tiny utility library for manipulating INI files with a straightforward
32 | // API and a minimal footprint. It conforms to the (somewhat) standard INI
33 | // format - sections and keys are case insensitive and all leading and
34 | // trailing whitespace is ignored. Comments are lines that begin with a
35 | // semicolon. Trailing comments are allowed on section lines.
36 | //
37 | // Files are read on demand, upon which data is kept in memory and the file
38 | // is closed. This utility supports lazy writing, which only writes changes
39 | // and updates to a file and preserves custom formatting and comments. A lazy
40 | // write invoked by a write() call will read the output file, find what
41 | // changes have been made and update the file accordingly. If you only need to
42 | // generate files, use generate() instead. Section and key order is preserved
43 | // on read, write and insert.
44 | //
45 | ///////////////////////////////////////////////////////////////////////////////
46 | //
47 | // /* BASIC USAGE EXAMPLE: */
48 | //
49 | // /* read from file */
50 | // mINI::INIFile file("myfile.ini");
51 | // mINI::INIStructure ini;
52 | // file.read(ini);
53 | //
54 | // /* read value; gets a reference to actual value in the structure.
55 | // if key or section don't exist, a new empty value will be created */
56 | // std::string& value = ini["section"]["key"];
57 | //
58 | // /* read value safely; gets a copy of value in the structure.
59 | // does not alter the structure */
60 | // std::string value = ini.get("section").get("key");
61 | //
62 | // /* set or update values */
63 | // ini["section"]["key"] = "value";
64 | //
65 | // /* set multiple values */
66 | // ini["section2"].set({
67 | // {"key1", "value1"},
68 | // {"key2", "value2"}
69 | // });
70 | //
71 | // /* write updates back to file, preserving comments and formatting */
72 | // file.write(ini);
73 | //
74 | // /* or generate a file (overwrites the original) */
75 | // file.generate(ini);
76 | //
77 | ///////////////////////////////////////////////////////////////////////////////
78 | //
79 | // Long live the INI file!!!
80 | //
81 | ///////////////////////////////////////////////////////////////////////////////
82 |
83 | #ifndef MINI_INI_H_
84 | #define MINI_INI_H_
85 |
86 | #include
87 | #include
88 | #include
89 | #include
90 | #include
91 | #include
92 | #include
93 | #include
94 | #include
95 | #include
96 |
97 | namespace mINI
98 | {
99 | namespace INIStringUtil
100 | {
101 | const char* const whitespaceDelimiters = " \t\n\r\f\v";
102 | inline void trim(std::string& str)
103 | {
104 | str.erase(str.find_last_not_of(whitespaceDelimiters) + 1);
105 | str.erase(0, str.find_first_not_of(whitespaceDelimiters));
106 | }
107 | #ifndef MINI_CASE_SENSITIVE
108 | inline void toLower(std::string& str)
109 | {
110 | std::transform(str.begin(), str.end(), str.begin(), [](const char c) {
111 | return static_cast(std::tolower(c));
112 | });
113 | }
114 | #endif
115 | inline void replace(std::string& str, std::string const& a, std::string const& b)
116 | {
117 | if (!a.empty())
118 | {
119 | std::size_t pos = 0;
120 | while ((pos = str.find(a, pos)) != std::string::npos)
121 | {
122 | str.replace(pos, a.size(), b);
123 | pos += b.size();
124 | }
125 | }
126 | }
127 | #ifdef _WIN32
128 | const char* const endl = "\r\n";
129 | #else
130 | const char* const endl = "\n";
131 | #endif
132 | }
133 |
134 | template
135 | class INIMap
136 | {
137 | private:
138 | using T_DataIndexMap = std::unordered_map;
139 | using T_DataItem = std::pair;
140 | using T_DataContainer = std::vector;
141 | using T_MultiArgs = typename std::vector>;
142 |
143 | T_DataIndexMap dataIndexMap;
144 | T_DataContainer data;
145 |
146 | inline std::size_t setEmpty(std::string& key)
147 | {
148 | std::size_t index = data.size();
149 | dataIndexMap[key] = index;
150 | data.emplace_back(key, T());
151 | return index;
152 | }
153 |
154 | public:
155 | using const_iterator = typename T_DataContainer::const_iterator;
156 |
157 | INIMap() { }
158 |
159 | INIMap(INIMap const& other)
160 | {
161 | std::size_t data_size = other.data.size();
162 | for (std::size_t i = 0; i < data_size; ++i)
163 | {
164 | auto const& key = other.data[i].first;
165 | auto const& obj = other.data[i].second;
166 | data.emplace_back(key, obj);
167 | }
168 | dataIndexMap = T_DataIndexMap(other.dataIndexMap);
169 | }
170 |
171 | T& operator[](std::string key)
172 | {
173 | INIStringUtil::trim(key);
174 | #ifndef MINI_CASE_SENSITIVE
175 | INIStringUtil::toLower(key);
176 | #endif
177 | auto it = dataIndexMap.find(key);
178 | bool hasIt = (it != dataIndexMap.end());
179 | std::size_t index = (hasIt) ? it->second : setEmpty(key);
180 | return data[index].second;
181 | }
182 | T get(std::string key) const
183 | {
184 | INIStringUtil::trim(key);
185 | #ifndef MINI_CASE_SENSITIVE
186 | INIStringUtil::toLower(key);
187 | #endif
188 | auto it = dataIndexMap.find(key);
189 | if (it == dataIndexMap.end())
190 | {
191 | return T();
192 | }
193 | return T(data[it->second].second);
194 | }
195 | bool has(std::string key) const
196 | {
197 | INIStringUtil::trim(key);
198 | #ifndef MINI_CASE_SENSITIVE
199 | INIStringUtil::toLower(key);
200 | #endif
201 | return (dataIndexMap.count(key) == 1);
202 | }
203 | void set(std::string key, T obj)
204 | {
205 | INIStringUtil::trim(key);
206 | #ifndef MINI_CASE_SENSITIVE
207 | INIStringUtil::toLower(key);
208 | #endif
209 | auto it = dataIndexMap.find(key);
210 | if (it != dataIndexMap.end())
211 | {
212 | data[it->second].second = obj;
213 | }
214 | else
215 | {
216 | dataIndexMap[key] = data.size();
217 | data.emplace_back(key, obj);
218 | }
219 | }
220 | void set(T_MultiArgs const& multiArgs)
221 | {
222 | for (auto const& it : multiArgs)
223 | {
224 | auto const& key = it.first;
225 | auto const& obj = it.second;
226 | set(key, obj);
227 | }
228 | }
229 | bool remove(std::string key)
230 | {
231 | INIStringUtil::trim(key);
232 | #ifndef MINI_CASE_SENSITIVE
233 | INIStringUtil::toLower(key);
234 | #endif
235 | auto it = dataIndexMap.find(key);
236 | if (it != dataIndexMap.end())
237 | {
238 | std::size_t index = it->second;
239 | data.erase(data.begin() + index);
240 | dataIndexMap.erase(it);
241 | for (auto& it2 : dataIndexMap)
242 | {
243 | auto& vi = it2.second;
244 | if (vi > index)
245 | {
246 | vi--;
247 | }
248 | }
249 | return true;
250 | }
251 | return false;
252 | }
253 | void clear()
254 | {
255 | data.clear();
256 | dataIndexMap.clear();
257 | }
258 | std::size_t size() const
259 | {
260 | return data.size();
261 | }
262 | const_iterator begin() const { return data.begin(); }
263 | const_iterator end() const { return data.end(); }
264 | };
265 |
266 | using INIStructure = INIMap>;
267 |
268 | namespace INIParser
269 | {
270 | using T_ParseValues = std::pair;
271 |
272 | enum class PDataType : char
273 | {
274 | PDATA_NONE,
275 | PDATA_COMMENT,
276 | PDATA_SECTION,
277 | PDATA_KEYVALUE,
278 | PDATA_UNKNOWN
279 | };
280 |
281 | inline PDataType parseLine(std::string line, T_ParseValues& parseData)
282 | {
283 | parseData.first.clear();
284 | parseData.second.clear();
285 | INIStringUtil::trim(line);
286 | if (line.empty())
287 | {
288 | return PDataType::PDATA_NONE;
289 | }
290 | char firstCharacter = line[0];
291 | if (firstCharacter == ';')
292 | {
293 | return PDataType::PDATA_COMMENT;
294 | }
295 | if (firstCharacter == '[')
296 | {
297 | auto commentAt = line.find_first_of(';');
298 | if (commentAt != std::string::npos)
299 | {
300 | line = line.substr(0, commentAt);
301 | }
302 | auto closingBracketAt = line.find_last_of(']');
303 | if (closingBracketAt != std::string::npos)
304 | {
305 | auto section = line.substr(1, closingBracketAt - 1);
306 | INIStringUtil::trim(section);
307 | parseData.first = section;
308 | return PDataType::PDATA_SECTION;
309 | }
310 | }
311 | auto lineNorm = line;
312 | INIStringUtil::replace(lineNorm, "\\=", " ");
313 | auto equalsAt = lineNorm.find_first_of('=');
314 | if (equalsAt != std::string::npos)
315 | {
316 | auto key = line.substr(0, equalsAt);
317 | INIStringUtil::trim(key);
318 | INIStringUtil::replace(key, "\\=", "=");
319 | auto value = line.substr(equalsAt + 1);
320 | INIStringUtil::trim(value);
321 | parseData.first = key;
322 | parseData.second = value;
323 | return PDataType::PDATA_KEYVALUE;
324 | }
325 | return PDataType::PDATA_UNKNOWN;
326 | }
327 | }
328 |
329 | class INIReader
330 | {
331 | public:
332 | using T_LineData = std::vector;
333 | using T_LineDataPtr = std::shared_ptr;
334 |
335 | bool isBOM = false;
336 |
337 | private:
338 | std::ifstream fileReadStream;
339 | T_LineDataPtr lineData;
340 |
341 | T_LineData readFile()
342 | {
343 | fileReadStream.seekg(0, std::ios::end);
344 | const std::size_t fileSize = static_cast(fileReadStream.tellg());
345 | fileReadStream.seekg(0, std::ios::beg);
346 | if (fileSize >= 3) {
347 | const char header[3] = {
348 | static_cast(fileReadStream.get()),
349 | static_cast(fileReadStream.get()),
350 | static_cast(fileReadStream.get())
351 | };
352 | isBOM = (
353 | header[0] == static_cast(0xEF) &&
354 | header[1] == static_cast(0xBB) &&
355 | header[2] == static_cast(0xBF)
356 | );
357 | }
358 | else {
359 | isBOM = false;
360 | }
361 | std::string fileContents;
362 | fileContents.resize(fileSize);
363 | fileReadStream.seekg(isBOM ? 3 : 0, std::ios::beg);
364 | fileReadStream.read(&fileContents[0], fileSize);
365 | fileReadStream.close();
366 | T_LineData output;
367 | if (fileSize == 0)
368 | {
369 | return output;
370 | }
371 | std::string buffer;
372 | buffer.reserve(50);
373 | for (std::size_t i = 0; i < fileSize; ++i)
374 | {
375 | char& c = fileContents[i];
376 | if (c == '\n')
377 | {
378 | output.emplace_back(buffer);
379 | buffer.clear();
380 | continue;
381 | }
382 | if (c != '\0' && c != '\r')
383 | {
384 | buffer += c;
385 | }
386 | }
387 | output.emplace_back(buffer);
388 | return output;
389 | }
390 |
391 | public:
392 | INIReader(std::string const& filename, bool keepLineData = false)
393 | {
394 | fileReadStream.open(filename, std::ios::in | std::ios::binary);
395 | if (keepLineData)
396 | {
397 | lineData = std::make_shared();
398 | }
399 | }
400 | ~INIReader() { }
401 |
402 | bool operator>>(INIStructure& data)
403 | {
404 | if (!fileReadStream.is_open())
405 | {
406 | return false;
407 | }
408 | T_LineData fileLines = readFile();
409 | std::string section;
410 | bool inSection = false;
411 | INIParser::T_ParseValues parseData;
412 | for (auto const& line : fileLines)
413 | {
414 | auto parseResult = INIParser::parseLine(line, parseData);
415 | if (parseResult == INIParser::PDataType::PDATA_SECTION)
416 | {
417 | inSection = true;
418 | data[section = parseData.first];
419 | }
420 | else if (inSection && parseResult == INIParser::PDataType::PDATA_KEYVALUE)
421 | {
422 | auto const& key = parseData.first;
423 | auto const& value = parseData.second;
424 | data[section][key] = value;
425 | }
426 | if (lineData && parseResult != INIParser::PDataType::PDATA_UNKNOWN)
427 | {
428 | if (parseResult == INIParser::PDataType::PDATA_KEYVALUE && !inSection)
429 | {
430 | continue;
431 | }
432 | lineData->emplace_back(line);
433 | }
434 | }
435 | return true;
436 | }
437 | T_LineDataPtr getLines()
438 | {
439 | return lineData;
440 | }
441 | };
442 |
443 | class INIGenerator
444 | {
445 | private:
446 | std::ofstream fileWriteStream;
447 |
448 | public:
449 | bool prettyPrint = false;
450 |
451 | INIGenerator(std::string const& filename)
452 | {
453 | fileWriteStream.open(filename, std::ios::out | std::ios::binary);
454 | }
455 | ~INIGenerator() { }
456 |
457 | bool operator<<(INIStructure const& data)
458 | {
459 | if (!fileWriteStream.is_open())
460 | {
461 | return false;
462 | }
463 | if (!data.size())
464 | {
465 | return true;
466 | }
467 | auto it = data.begin();
468 | for (;;)
469 | {
470 | auto const& section = it->first;
471 | auto const& collection = it->second;
472 | fileWriteStream
473 | << "["
474 | << section
475 | << "]";
476 | if (collection.size())
477 | {
478 | fileWriteStream << INIStringUtil::endl;
479 | auto it2 = collection.begin();
480 | for (;;)
481 | {
482 | auto key = it2->first;
483 | INIStringUtil::replace(key, "=", "\\=");
484 | auto value = it2->second;
485 | INIStringUtil::trim(value);
486 | fileWriteStream
487 | << key
488 | << ((prettyPrint) ? " = " : "=")
489 | << value;
490 | if (++it2 == collection.end())
491 | {
492 | break;
493 | }
494 | fileWriteStream << INIStringUtil::endl;
495 | }
496 | }
497 | if (++it == data.end())
498 | {
499 | break;
500 | }
501 | fileWriteStream << INIStringUtil::endl;
502 | if (prettyPrint)
503 | {
504 | fileWriteStream << INIStringUtil::endl;
505 | }
506 | }
507 | return true;
508 | }
509 | };
510 |
511 | class INIWriter
512 | {
513 | private:
514 | using T_LineData = std::vector;
515 | using T_LineDataPtr = std::shared_ptr;
516 |
517 | std::string filename;
518 |
519 | T_LineData getLazyOutput(T_LineDataPtr const& lineData, INIStructure& data, INIStructure& original)
520 | {
521 | T_LineData output;
522 | INIParser::T_ParseValues parseData;
523 | std::string sectionCurrent;
524 | bool parsingSection = false;
525 | bool continueToNextSection = false;
526 | bool discardNextEmpty = false;
527 | bool writeNewKeys = false;
528 | std::size_t lastKeyLine = 0;
529 | for (auto line = lineData->begin(); line != lineData->end(); ++line)
530 | {
531 | if (!writeNewKeys)
532 | {
533 | auto parseResult = INIParser::parseLine(*line, parseData);
534 | if (parseResult == INIParser::PDataType::PDATA_SECTION)
535 | {
536 | if (parsingSection)
537 | {
538 | writeNewKeys = true;
539 | parsingSection = false;
540 | --line;
541 | continue;
542 | }
543 | sectionCurrent = parseData.first;
544 | if (data.has(sectionCurrent))
545 | {
546 | parsingSection = true;
547 | continueToNextSection = false;
548 | discardNextEmpty = false;
549 | output.emplace_back(*line);
550 | lastKeyLine = output.size();
551 | }
552 | else
553 | {
554 | continueToNextSection = true;
555 | discardNextEmpty = true;
556 | continue;
557 | }
558 | }
559 | else if (parseResult == INIParser::PDataType::PDATA_KEYVALUE)
560 | {
561 | if (continueToNextSection)
562 | {
563 | continue;
564 | }
565 | if (data.has(sectionCurrent))
566 | {
567 | auto& collection = data[sectionCurrent];
568 | auto const& key = parseData.first;
569 | auto const& value = parseData.second;
570 | if (collection.has(key))
571 | {
572 | auto outputValue = collection[key];
573 | if (value == outputValue)
574 | {
575 | output.emplace_back(*line);
576 | }
577 | else
578 | {
579 | INIStringUtil::trim(outputValue);
580 | auto lineNorm = *line;
581 | INIStringUtil::replace(lineNorm, "\\=", " ");
582 | auto equalsAt = lineNorm.find_first_of('=');
583 | auto valueAt = lineNorm.find_first_not_of(
584 | INIStringUtil::whitespaceDelimiters,
585 | equalsAt + 1
586 | );
587 | std::string outputLine = line->substr(0, valueAt);
588 | if (prettyPrint && equalsAt + 1 == valueAt)
589 | {
590 | outputLine += " ";
591 | }
592 | outputLine += outputValue;
593 | output.emplace_back(outputLine);
594 | }
595 | lastKeyLine = output.size();
596 | }
597 | }
598 | }
599 | else
600 | {
601 | if (discardNextEmpty && line->empty())
602 | {
603 | discardNextEmpty = false;
604 | }
605 | else if (parseResult != INIParser::PDataType::PDATA_UNKNOWN)
606 | {
607 | output.emplace_back(*line);
608 | }
609 | }
610 | }
611 | if (writeNewKeys || std::next(line) == lineData->end())
612 | {
613 | T_LineData linesToAdd;
614 | if (data.has(sectionCurrent) && original.has(sectionCurrent))
615 | {
616 | auto const& collection = data[sectionCurrent];
617 | auto const& collectionOriginal = original[sectionCurrent];
618 | for (auto const& it : collection)
619 | {
620 | auto key = it.first;
621 | if (collectionOriginal.has(key))
622 | {
623 | continue;
624 | }
625 | auto value = it.second;
626 | INIStringUtil::replace(key, "=", "\\=");
627 | INIStringUtil::trim(value);
628 | linesToAdd.emplace_back(
629 | key + ((prettyPrint) ? " = " : "=") + value
630 | );
631 | }
632 | }
633 | if (!linesToAdd.empty())
634 | {
635 | output.insert(
636 | output.begin() + lastKeyLine,
637 | linesToAdd.begin(),
638 | linesToAdd.end()
639 | );
640 | }
641 | if (writeNewKeys)
642 | {
643 | writeNewKeys = false;
644 | --line;
645 | }
646 | }
647 | }
648 | for (auto const& it : data)
649 | {
650 | auto const& section = it.first;
651 | if (original.has(section))
652 | {
653 | continue;
654 | }
655 | if (prettyPrint && output.size() > 0 && !output.back().empty())
656 | {
657 | output.emplace_back();
658 | }
659 | output.emplace_back("[" + section + "]");
660 | auto const& collection = it.second;
661 | for (auto const& it2 : collection)
662 | {
663 | auto key = it2.first;
664 | auto value = it2.second;
665 | INIStringUtil::replace(key, "=", "\\=");
666 | INIStringUtil::trim(value);
667 | output.emplace_back(
668 | key + ((prettyPrint) ? " = " : "=") + value
669 | );
670 | }
671 | }
672 | return output;
673 | }
674 |
675 | public:
676 | bool prettyPrint = false;
677 |
678 | INIWriter(std::string const& filename)
679 | : filename(filename)
680 | {
681 | }
682 | ~INIWriter() { }
683 |
684 | bool operator<<(INIStructure& data)
685 | {
686 | struct stat buf;
687 | bool fileExists = (stat(filename.c_str(), &buf) == 0);
688 | if (!fileExists)
689 | {
690 | INIGenerator generator(filename);
691 | generator.prettyPrint = prettyPrint;
692 | return generator << data;
693 | }
694 | INIStructure originalData;
695 | T_LineDataPtr lineData;
696 | bool readSuccess = false;
697 | bool fileIsBOM = false;
698 | {
699 | INIReader reader(filename, true);
700 | if ((readSuccess = reader >> originalData))
701 | {
702 | lineData = reader.getLines();
703 | fileIsBOM = reader.isBOM;
704 | }
705 | }
706 | if (!readSuccess)
707 | {
708 | return false;
709 | }
710 | T_LineData output = getLazyOutput(lineData, data, originalData);
711 | std::ofstream fileWriteStream(filename, std::ios::out | std::ios::binary);
712 | if (fileWriteStream.is_open())
713 | {
714 | if (fileIsBOM) {
715 | const char utf8_BOM[3] = {
716 | static_cast(0xEF),
717 | static_cast(0xBB),
718 | static_cast(0xBF)
719 | };
720 | fileWriteStream.write(utf8_BOM, 3);
721 | }
722 | if (output.size())
723 | {
724 | auto line = output.begin();
725 | for (;;)
726 | {
727 | fileWriteStream << *line;
728 | if (++line == output.end())
729 | {
730 | break;
731 | }
732 | fileWriteStream << INIStringUtil::endl;
733 | }
734 | }
735 | return true;
736 | }
737 | return false;
738 | }
739 | };
740 |
741 | class INIFile
742 | {
743 | private:
744 | std::string filename;
745 |
746 | public:
747 | INIFile(std::string const& filename)
748 | : filename(filename)
749 | { }
750 |
751 | ~INIFile() { }
752 |
753 | bool read(INIStructure& data) const
754 | {
755 | if (data.size())
756 | {
757 | data.clear();
758 | }
759 | if (filename.empty())
760 | {
761 | return false;
762 | }
763 | INIReader reader(filename);
764 | return reader >> data;
765 | }
766 | bool generate(INIStructure const& data, bool pretty = false) const
767 | {
768 | if (filename.empty())
769 | {
770 | return false;
771 | }
772 | INIGenerator generator(filename);
773 | generator.prettyPrint = pretty;
774 | return generator << data;
775 | }
776 | bool write(INIStructure& data, bool pretty = false) const
777 | {
778 | if (filename.empty())
779 | {
780 | return false;
781 | }
782 | INIWriter writer(filename);
783 | writer.prettyPrint = pretty;
784 | return writer << data;
785 | }
786 | };
787 | }
788 |
789 | #endif // MINI_INI_H_
790 |
--------------------------------------------------------------------------------
/MGS3CrouchWalk/types.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | const enum PlayerMotion
6 | {
7 | Stand = 0,
8 | Squat = 2,
9 | StandMoveSlow = 3,
10 | StandMoveStalk = 4,
11 | Run = 5,
12 | RunUpwards = 6,
13 | Ground = 9,
14 | GroundMove = 10,
15 | RunToRolling = 21,
16 | RollingToStand = 22,
17 | RollingToGround = 23,
18 | StandCover = 97,
19 | SquatCover = 98,
20 | SquatMove = 199,
21 | SquatMoveSlow = 200
22 | };
23 |
24 | const enum MovementFlag
25 | {
26 | FlagWalk = 0x20u,
27 | FlagSquatToGround = 0x30u,
28 | FlagStand = 0x40u,
29 | };
30 |
31 | struct MovementWork
32 | {
33 | uint32_t field00;
34 | uint32_t field04;
35 | uint32_t field08;
36 | uint32_t field0C;
37 | uint32_t field10;
38 | uint32_t field14;
39 | uint32_t field18;
40 | uint32_t field1C;
41 | uint32_t field20;
42 | uint32_t field24;
43 | uint32_t field28;
44 | uint32_t field2C;
45 | uint32_t field30;
46 | uint32_t field34;
47 | uint32_t field38;
48 | uint32_t field3C;
49 | uint32_t field40;
50 | uint32_t field44;
51 | uint32_t field48;
52 | uint32_t field4C;
53 | uint32_t field50;
54 | uint32_t field54;
55 | uintptr_t action;
56 | uint32_t motion;
57 | uint32_t field64;
58 | uint32_t flag;
59 | uint16_t padCount;
60 | uint16_t field6C;
61 | uint16_t turnDir;
62 | uint32_t field70;
63 | uint32_t field74;
64 | uint32_t field78;
65 | uint32_t field7C;
66 | uint32_t field80;
67 | uint32_t field84;
68 | uint32_t field88;
69 | uint16_t padForceLimit;
70 | };
71 |
72 | struct MtcmControl
73 | {
74 | int motion;
75 | int flag;
76 | void* mtarHeader;
77 | void* mtcmHeader;
78 | uintptr_t* data;
79 | int loop;
80 | int lastCheckTime;
81 | uint32_t field24;
82 | uint32_t field28;
83 | uint32_t field2C;
84 | uint32_t field30;
85 | uint32_t field34;
86 | uint32_t field38;
87 | float field3C;
88 | float field40;
89 | float field44;
90 | float motionTimeBase;
91 | uint32_t field4C;
92 | uint32_t mtarName;
93 | };
94 |
95 | struct MotionControl
96 | {
97 | void* mtar;
98 | MtcmControl* mtcmControl;
99 | };
100 |
101 | typedef uintptr_t* __fastcall InitializeCamoIndexDelegate(int* a1, int a2);
102 | typedef int* __fastcall CalculateCamoIndexDelegate(int* a1, int a2);
103 | typedef int* __fastcall ActionSquatStillDelegate(int64_t work, MovementWork* plWork, int64_t a3, int64_t a4);
104 | typedef uint32_t __fastcall PlayerSetMotionDelegate(int64_t work, PlayerMotion motion);
105 | typedef void __fastcall SetMotionDataDelegate(MotionControl* motionControl, int layer, int motion, int time, int64_t mask);
106 | typedef int64_t __fastcall PlayerStatusCheckDelegate(unsigned int a1);
107 | typedef int64_t __fastcall ActMovementDelegate(MovementWork* plWork, int64_t work, int flag);
108 | typedef int64_t __fastcall GetButtonHoldingStateDelegate(int64_t work, MovementWork* plWork);
109 | typedef int64_t __fastcall PlayerStatusSetDelegate(int a1, int64_t a2, int64_t a3, int64_t a4);
110 | typedef int64_t __fastcall MotionPlaySequenceDelegate(__int64 mtsqCtrl, int layer, int num, int flag, int loop_time);
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MGS3CrouchWalk
2 | A plugin that aims to bring crouch walking to Metal Gear Solid 3: Master Collection.
3 |
4 | **Download:** [GitHub](https://github.com/cipherxof/MGS3CrouchWalk/releases) | [NexusMods](https://www.nexusmods.com/metalgearsolidmastercollection/mods/118/)
5 |
6 | 
7 |
8 | ## Features
9 | - Both slow and fast crouch walking are implemented.
10 | - NPCs are only alerted while fast crouch walking in close proximity.
11 | - Crouching affects your visibility to enemies (i.e, you can hide behind cover)
12 | - The camo index is dynamically adjusted while crouch walking.
13 | - Seamlessly integrated into the game engine as a genuine feature, without displacing any existing functionality.
14 |
15 | ## Credits
16 | - Zoft for mtar research and porting the 3DS animations over to the Master Collection
17 |
--------------------------------------------------------------------------------
/libMinHook.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cipherxof/MGS3CrouchWalk/fc0ca548305b7752771425c0cbdd03e9c162a3ae/libMinHook.lib
--------------------------------------------------------------------------------