├── .gitattributes
├── .gitignore
├── ManualReload.sln
└── ManualReload
├── ManualReload.rc
├── ManualReload.vcxproj
├── ManualReload.vcxproj.filters
├── ManualReload.vcxproj.user
├── Utility
├── General.cpp
├── General.h
├── Hooking.cpp
├── Hooking.h
├── Logger.cpp
├── Logger.h
├── UIText.cpp
├── UIText.h
├── config.h
├── patch.h
└── pattern.h
├── dllmain.cpp
├── main.cpp
├── res
├── weap_dryfire_rifle.wav
└── weap_dryfire_smg.wav
├── resource.h
├── stdafx.cpp
├── stdafx.h
├── strings.h
└── types.h
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # Windows shortcuts
18 | *.lnk
19 |
20 | # =========================
21 | # Operating System Files
22 | # =========================
23 |
24 | # OSX
25 | # =========================
26 |
27 | .DS_Store
28 | .AppleDouble
29 | .LSOverride
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear in the root of a volume
35 | .DocumentRevisions-V100
36 | .fseventsd
37 | .Spotlight-V100
38 | .TemporaryItems
39 | .Trashes
40 | .VolumeIcon.icns
41 |
42 | # Directories potentially created on remote AFP share
43 | .AppleDB
44 | .AppleDesktop
45 | Network Trash Folder
46 | Temporary Items
47 | .apdisk
48 | /.vs/ManualReload/v16
49 |
--------------------------------------------------------------------------------
/ManualReload.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ManualReload", "ManualReload\ManualReload.vcxproj", "{E8CB038E-B8C3-4654-9E52-371637319500}"
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 | {E8CB038E-B8C3-4654-9E52-371637319500}.Debug|x64.ActiveCfg = Debug|x64
17 | {E8CB038E-B8C3-4654-9E52-371637319500}.Debug|x64.Build.0 = Debug|x64
18 | {E8CB038E-B8C3-4654-9E52-371637319500}.Debug|x86.ActiveCfg = Debug|Win32
19 | {E8CB038E-B8C3-4654-9E52-371637319500}.Debug|x86.Build.0 = Debug|Win32
20 | {E8CB038E-B8C3-4654-9E52-371637319500}.Release|x64.ActiveCfg = Release|x64
21 | {E8CB038E-B8C3-4654-9E52-371637319500}.Release|x64.Build.0 = Release|x64
22 | {E8CB038E-B8C3-4654-9E52-371637319500}.Release|x86.ActiveCfg = Release|Win32
23 | {E8CB038E-B8C3-4654-9E52-371637319500}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/ManualReload/ManualReload.rc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CamxxCore/RealisticReload/cc902a6c0b6ab937a2320faffd331517358baeaa/ManualReload/ManualReload.rc
--------------------------------------------------------------------------------
/ManualReload/ManualReload.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 | {E8CB038E-B8C3-4654-9E52-371637319500}
23 | Win32Proj
24 | ManualReload
25 | 10.0.10240.0
26 |
27 |
28 |
29 | DynamicLibrary
30 | true
31 | v140
32 | MultiByte
33 |
34 |
35 | DynamicLibrary
36 | false
37 | v140
38 | true
39 | MultiByte
40 |
41 |
42 | DynamicLibrary
43 | true
44 | v140
45 | MultiByte
46 |
47 |
48 | DynamicLibrary
49 | false
50 | v140
51 | true
52 | MultiByte
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | true
74 | .asi
75 | $(SolutionDir)Build\$(Configuration)\
76 | $(SolutionDir)Build\Intermediate\$(Configuration)\
77 |
78 |
79 | true
80 | .asi
81 | $(SolutionDir)Build\$(Configuration)\
82 | $(SolutionDir)Build\Intermediate\$(Configuration)\
83 |
84 |
85 | false
86 | .asi
87 | $(SolutionDir)Build\$(Configuration)\
88 | $(SolutionDir)Build\Intermediate\$(Configuration)\
89 |
90 |
91 | false
92 | .asi
93 | $(SolutionDir)Build\$(Configuration)\
94 | $(SolutionDir)Build\Intermediate\$(Configuration)\
95 |
96 |
97 |
98 | Use
99 | Level3
100 | Disabled
101 | WIN32;_DEBUG;_WINDOWS;_USRDLL;MANUALRELOAD_EXPORTS;%(PreprocessorDefinitions)
102 | true
103 | Speed
104 | $(ProjectDir)
105 |
106 |
107 | Windows
108 | true
109 | lib/ScriptHookV.lib
110 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Winmm.lib;Winmm.lib;%(AdditionalDependencies)
111 |
112 |
113 | copy "$(TargetPath)" "C:\Program Files\Rockstar Games\Grand Theft Auto V\$(ProjectName).asi"
114 |
115 |
116 |
117 |
118 | Use
119 | Level3
120 | Disabled
121 | _DEBUG;_WINDOWS;_USRDLL;MANUALRELOAD_EXPORTS;%(PreprocessorDefinitions)
122 | true
123 | Speed
124 | $(ProjectDir)
125 |
126 |
127 | Windows
128 | true
129 | lib/ScriptHookV.lib
130 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Winmm.lib;Winmm.lib;%(AdditionalDependencies)
131 |
132 |
133 | copy "$(TargetPath)" "C:\Program Files\Rockstar Games\Grand Theft Auto V\$(ProjectName).asi"
134 |
135 |
136 |
137 |
138 | Level3
139 | Use
140 | MaxSpeed
141 | true
142 | true
143 | WIN32;NDEBUG;_WINDOWS;_USRDLL;MANUALRELOAD_EXPORTS;%(PreprocessorDefinitions)
144 | true
145 | Speed
146 | $(ProjectDir)
147 |
148 |
149 | Windows
150 | true
151 | true
152 | true
153 | lib/ScriptHookV.lib
154 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Winmm.lib;%(AdditionalDependencies)
155 |
156 |
157 | copy "$(TargetPath)" "C:\Program Files\Rockstar Games\Grand Theft Auto V\$(ProjectName).asi"
158 |
159 |
160 |
161 |
162 | Level3
163 | Use
164 | MaxSpeed
165 | true
166 | true
167 | NDEBUG;_WINDOWS;_USRDLL;MANUALRELOAD_EXPORTS;%(PreprocessorDefinitions)
168 | true
169 | Speed
170 | $(ProjectDir)
171 |
172 |
173 | Windows
174 | true
175 | true
176 | true
177 | lib/ScriptHookV.lib
178 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Winmm.lib;%(AdditionalDependencies)
179 |
180 |
181 | copy "$(TargetPath)" "C:\Program Files\Rockstar Games\Grand Theft Auto V\$(ProjectName).asi"
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 | false
200 |
201 |
202 | false
203 |
204 |
205 | false
206 |
207 |
208 | false
209 |
210 |
211 |
212 |
213 |
214 | Create
215 | Create
216 | Create
217 | Create
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
--------------------------------------------------------------------------------
/ManualReload/ManualReload.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
6 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
7 |
8 |
9 | {d001c3a2-c96e-4d99-9c0c-a8a826e442b9}
10 |
11 |
12 |
13 |
14 | Utility
15 |
16 |
17 | Utility
18 |
19 |
20 | Utility
21 |
22 |
23 | Utility
24 |
25 |
26 | Utility
27 |
28 |
29 | Utility
30 |
31 |
32 | Utility
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | Utility
44 |
45 |
46 | Utility
47 |
48 |
49 | Utility
50 |
51 |
52 | Utility
53 |
54 |
55 | Utility
56 |
57 |
58 |
59 |
60 | Resource Files
61 |
62 |
63 |
64 |
65 | Resource Files
66 |
67 |
68 | Resource Files
69 |
70 |
71 |
--------------------------------------------------------------------------------
/ManualReload/ManualReload.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ManualReload/Utility/General.cpp:
--------------------------------------------------------------------------------
1 | #include "stdafx.h"
2 | #include
3 | #include
4 | #include
5 |
6 | HMODULE Utility::GetActiveModule() {
7 | HMODULE hModule = NULL;
8 |
9 | GetModuleHandleEx( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
10 | reinterpret_cast( &GetActiveModule ),
11 | &hModule );
12 |
13 | return hModule;
14 | }
15 |
16 | std::string Utility::GetModuleName( HMODULE hModule ) {
17 | TCHAR inBuf[MAX_PATH];
18 |
19 | if ( !hModule )
20 | hModule = GetActiveModule();
21 |
22 | GetModuleFileName( hModule, inBuf, MAX_PATH );
23 |
24 | auto str = std::string( inBuf );
25 |
26 | auto seperator = str.find_last_of( "\\" );
27 |
28 | if ( seperator != std::string::npos )
29 | seperator += 1;
30 |
31 | return str.substr( seperator, str.find_last_of( "." ) - seperator );
32 | }
33 |
34 | std::string Utility::GetWorkingDirectory() {
35 | HMODULE hModule = GetActiveModule();
36 |
37 | TCHAR inBuf[MAX_PATH];
38 |
39 | GetModuleFileName( hModule, inBuf, MAX_PATH );
40 |
41 | auto str = std::string( inBuf );
42 |
43 | auto seperator = str.find_last_of( "\\" );
44 |
45 | if ( seperator != std::string::npos )
46 | seperator += 1;
47 |
48 | return str.substr( 0, seperator );
49 |
50 | }
51 |
52 | std::string Utility::GetShortTimeString() {
53 | time_t t = time( NULL );
54 |
55 | struct tm timeinfo;
56 |
57 | localtime_s( &timeinfo, &t );
58 |
59 | return FormatString( "%02d:%02d:%02d",
60 | timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec );
61 | }
62 |
63 | bool Utility::FileExists( std::string fileName ) {
64 | std::ifstream infile( fileName );
65 |
66 | return infile.good();
67 | }
68 |
69 | void Utility::SplitString( std::string str, std::string splitBy, std::vector& tokens ) {
70 | tokens.push_back( str );
71 |
72 | auto splitLen = splitBy.size();
73 |
74 | while ( true ) {
75 | auto frag = tokens.back();
76 |
77 | auto splitAt = frag.find( splitBy );
78 |
79 | if ( splitAt == std::string::npos ) {
80 | break;
81 | }
82 |
83 | tokens.back() = frag.substr( 0, splitAt );
84 |
85 | tokens.push_back( frag.substr( splitAt + splitLen, frag.size() - ( splitAt + splitLen ) ) );
86 | }
87 | }
88 |
89 | void Utility::ToLower( std::string& str ) {
90 | transform( str.begin(), str.end(), str.begin(), std::tolower );
91 | }
92 |
93 | unsigned Utility::GetHashKey( std::string str ) {
94 | unsigned int hash = 0;
95 | for ( int i = 0; i < str.size(); ++i ) {
96 | hash += str[i];
97 | hash += hash << 10;
98 | hash ^= hash >> 6;
99 | }
100 | hash += hash << 3;
101 | hash ^= hash >> 11;
102 | hash += hash << 15;
103 | return hash;
104 | }
105 |
106 | unsigned int Utility::StringToHash( std::string str ) {
107 | char * p;
108 |
109 | unsigned long numericHash = strtoul( str.c_str(), &p, 10 );
110 |
111 | return *p != '\0' ? GetHashKey( str ) :
112 | static_cast( numericHash );
113 | }
114 |
115 | BOOL Utility::PlaySoundResource( int id, BOOL async ) {
116 | BOOL bRtn;
117 | HANDLE hRes;
118 |
119 | auto hInst = GetActiveModule();
120 |
121 | HRSRC hResInfo = FindResource( hInst, MAKEINTRESOURCE( id ), "WAVE" );
122 |
123 | if ( hResInfo == NULL )
124 | return FALSE;
125 |
126 | hRes = LoadResource( hInst, hResInfo );
127 | if ( hRes == NULL )
128 | return FALSE;
129 |
130 | LPSTR lpRes = ( char* )LockResource( hRes );
131 |
132 | if ( lpRes != NULL ) {
133 | bRtn = sndPlaySound( lpRes,
134 | SND_MEMORY | async | SND_NODEFAULT );
135 |
136 | UnlockResource( hRes );
137 | } else
138 | bRtn = 0;
139 |
140 | FreeResource( hRes );
141 |
142 | return bRtn;
143 | }
144 |
145 |
--------------------------------------------------------------------------------
/ManualReload/Utility/General.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace Utility {
4 | HMODULE GetActiveModule();
5 |
6 | std::string GetModuleName( HMODULE hModule );
7 |
8 | std::string GetWorkingDirectory();
9 |
10 | std::string GetShortTimeString();
11 |
12 | bool FileExists( std::string fileName );
13 |
14 | void SplitString( std::string str, std::string splitBy, std::vector& tokens );
15 |
16 | void ToLower( std::string& str );
17 |
18 | unsigned int GetHashKey( std::string str );
19 |
20 | unsigned int StringToHash( std::string str );
21 |
22 | template
23 | std::string FormatString( const std::string& format, Args ... args ) {
24 | size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1;
25 | std::unique_ptr buf( new char[size] );
26 | snprintf( buf.get(), size, format.c_str(), args ... );
27 | return std::string( buf.get(), buf.get() + size - 1 );
28 | }
29 |
30 | BOOL PlaySoundResource( int id, BOOL async );
31 | }
--------------------------------------------------------------------------------
/ManualReload/Utility/Hooking.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright(c) 2014 Bas Timmer / NTAuthority et al.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files(the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions :
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 | */
21 |
22 | #include "stdafx.h"
23 |
24 | // Size of each memory block. (= page size of VirtualAlloc)
25 | const uint64_t MEMORY_BLOCK_SIZE = 0x1000;
26 |
27 | // Max range for seeking a memory block. (= 1024MB)
28 | const uint64_t MAX_MEMORY_RANGE = 0x40000000;
29 |
30 | std::vector> HookManager::m_vecInstalledHooks;
31 |
32 | PBYTE HookManager::CreateTrampolineFunction( PVOID lpTarget ) {
33 | const auto pModule = GetModuleHandle( nullptr );
34 |
35 | return ( PBYTE )AllocateFunctionStub( pModule, lpTarget, 0 );
36 | }
37 |
38 | void HookManager::Uninititialize() {
39 | for ( auto & hook : m_vecInstalledHooks ) {
40 | if ( hook ) {
41 | hook->remove();
42 | }
43 | }
44 |
45 | LOG( "Removed %d hooks", m_vecInstalledHooks.size() );
46 |
47 | m_vecInstalledHooks.clear();
48 | }
49 |
50 | PVOID HookManager::AllocateFunctionStub( PVOID origin, PVOID function, int type ) {
51 | static void* g_currentStub = nullptr;
52 |
53 | if ( !g_currentStub ) {
54 | SYSTEM_INFO si;
55 | GetSystemInfo( &si );
56 |
57 | ULONG_PTR minAddr = ( ULONG_PTR )si.lpMinimumApplicationAddress;
58 |
59 | if ( ( ULONG_PTR )origin > MAX_MEMORY_RANGE &&
60 | minAddr < ( ULONG_PTR )origin - MAX_MEMORY_RANGE )
61 | minAddr = ( ULONG_PTR )origin - MAX_MEMORY_RANGE;
62 |
63 | LPVOID pAlloc = origin;
64 |
65 | while ( ( ULONG_PTR )pAlloc >= minAddr ) {
66 | pAlloc = FindPrevFreeRegion( pAlloc, ( LPVOID )minAddr,
67 | si.dwAllocationGranularity );
68 | if ( pAlloc == NULL )
69 | break;
70 |
71 | g_currentStub =
72 | VirtualAlloc( pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE,
73 | PAGE_EXECUTE_READWRITE );
74 | if ( g_currentStub != NULL )
75 | break;
76 | }
77 | }
78 | if ( !g_currentStub )
79 | return nullptr;
80 |
81 | char* code = ( char* )g_currentStub;
82 |
83 | *( uint8_t* )code = 0x48;
84 | *( uint8_t* )( code + 1 ) = 0xb8 | type;
85 |
86 | *( uint64_t* )( code + 2 ) = ( uint64_t )function;
87 |
88 | *( uint16_t* )( code + 10 ) = 0xE0FF | ( type << 8 );
89 |
90 | *( uint64_t* )( code + 12 ) = 0xCCCCCCCCCCCCCCCC;
91 |
92 | g_currentStub = ( void* )( ( uint64_t )g_currentStub + 20 );
93 |
94 | return code;
95 | }
96 |
97 | LPVOID HookManager::FindPrevFreeRegion( LPVOID pAddress,
98 | LPVOID pMinAddr,
99 | DWORD dwAllocationGranularity ) {
100 | ULONG_PTR tryAddr = ( ULONG_PTR )pAddress;
101 |
102 | // Round down to the next allocation granularity.
103 | tryAddr -= tryAddr % dwAllocationGranularity;
104 |
105 | // Start from the previous allocation granularity multiply.
106 | tryAddr -= dwAllocationGranularity;
107 |
108 | while ( tryAddr >= ( ULONG_PTR )pMinAddr ) {
109 | MEMORY_BASIC_INFORMATION mbi;
110 | if ( VirtualQuery( ( LPVOID )tryAddr, &mbi, sizeof( MEMORY_BASIC_INFORMATION ) ) ==
111 | 0 )
112 | break;
113 |
114 | if ( mbi.State == MEM_FREE )
115 | return ( LPVOID )tryAddr;
116 |
117 | if ( ( ULONG_PTR )mbi.AllocationBase < dwAllocationGranularity )
118 | break;
119 |
120 | tryAddr = ( ULONG_PTR )mbi.AllocationBase - dwAllocationGranularity;
121 | }
122 |
123 | return NULL;
124 | }
125 |
--------------------------------------------------------------------------------
/ManualReload/Utility/Hooking.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | class IHookBase {
4 | public:
5 | virtual void add( PBYTE address ) = 0;
6 | virtual void remove() = 0;
7 | };
8 |
9 | template
10 | class __declspec( novtable ) Hook : public IHookBase {
11 | public:
12 | Hook( PBYTE trampoline, T target ) : fn( target ), m_pTrampoline( trampoline ) { }
13 | T fn;
14 | void add( PBYTE address ) override = 0;
15 | void remove() override = 0;
16 | virtual ~Hook();
17 |
18 | PBYTE getTrampoline() const {
19 | return m_pTrampoline;
20 | }
21 |
22 | protected:
23 | PBYTE m_pTrampoline;
24 | std::vector addresses;
25 | };
26 |
27 | template
28 | Hook::~Hook() {
29 | }
30 |
31 | template
32 | void Hook::add( PBYTE address ) {
33 | addresses.push_back( address );
34 | }
35 |
36 | template
37 | class CallHook : public Hook {
38 | public:
39 | CallHook( PBYTE trampoline, T target ) : Hook( trampoline, target ) { }
40 | ~CallHook();
41 | void add( PBYTE address ) override;
42 | void remove() override;
43 | };
44 |
45 | template
46 | void CallHook::add( PBYTE address ) {
47 | *( BYTE* )address = 0xE8;
48 |
49 | *reinterpret_cast( address + 1 ) =
50 | static_cast( ( intptr_t )m_pTrampoline - ( intptr_t )address - 5 );
51 |
52 | Hook::add( address );
53 | }
54 |
55 | template
56 | void CallHook::remove() {
57 | for ( auto & address : addresses ) {
58 | *reinterpret_cast( address + 1 ) =
59 | static_cast( ( intptr_t )fn - ( intptr_t )address - 5 );
60 | }
61 | }
62 |
63 | template
64 | CallHook::~CallHook() {
65 | CallHook::remove();
66 | }
67 |
68 | template
69 | class JmpHook : public Hook {
70 | public:
71 | JmpHook( PBYTE trampoline, T target ) : Hook( trampoline, target ) { }
72 | ~JmpHook();
73 | void add( PBYTE address ) override;
74 | void remove() override;
75 | };
76 |
77 | template
78 | void JmpHook::add( PBYTE address ) {
79 | *reinterpret_cast( address + 1 ) =
80 | static_cast( ( intptr_t )m_pTrampoline - ( intptr_t )address - 5 );
81 |
82 | Hook::add( address );
83 | }
84 |
85 | template
86 | void JmpHook::remove() {
87 | for ( auto & address : addresses ) {
88 | *reinterpret_cast( address + 1 ) =
89 | static_cast( ( intptr_t )fn - ( intptr_t )address - 5 );
90 | }
91 | }
92 |
93 | template
94 | JmpHook::~JmpHook() {
95 | JmpHook::remove();
96 | }
97 |
98 | template
99 | class VirtualHook : public Hook {
100 | public:
101 | VirtualHook( PBYTE trampoline, T func ) : Hook( trampoline, func ) { }
102 | ~VirtualHook();
103 | void add( PBYTE address ) override;
104 | void remove() override;
105 | };
106 |
107 | template
108 | void VirtualHook::add( PBYTE address ) {
109 | *reinterpret_cast( address ) = m_pTrampoline;
110 |
111 | Hook::add( address );
112 | }
113 |
114 | template
115 | void VirtualHook::remove() {
116 | for ( auto & address : addresses ) {
117 | *reinterpret_cast( address ) = ( uintptr_t )fn;
118 | }
119 | }
120 |
121 | template
122 | VirtualHook::~VirtualHook() {
123 | VirtualHook::remove();
124 | }
125 |
126 | class HookManager {
127 | static std::vector> m_vecInstalledHooks;
128 |
129 | static PBYTE CreateTrampolineFunction( PVOID lpTarget );
130 |
131 | public:
132 | template
133 | static CallHook * SetCall( PBYTE address, T fn ) {
134 | T target = reinterpret_cast( *reinterpret_cast( address + 1 ) + ( address + 5 ) );
135 |
136 | auto pHook = new CallHook( CreateTrampolineFunction( fn ), target );
137 |
138 | pHook->add( address );
139 |
140 | m_vecInstalledHooks.push_back( std::unique_ptr( pHook ) );
141 |
142 | return pHook;
143 | }
144 |
145 | template
146 | static JmpHook * SetJmp( PBYTE address, T fn ) {
147 | T target = reinterpret_cast( *reinterpret_cast( address + 1 ) + ( address + 5 ) );
148 |
149 | auto pHook = new JmpHook( CreateTrampolineFunction( fn ), target );
150 |
151 | pHook->add( address );
152 |
153 | m_vecInstalledHooks.push_back( std::unique_ptr( pHook ) );
154 |
155 | return pHook;
156 | }
157 |
158 | template
159 | static VirtualHook * HookVirtual( PBYTE address, T fn ) {
160 | T target = *reinterpret_cast( address );
161 |
162 | auto pHook = new VirtualHook( CreateTrampolineFunction( fn ), target );
163 |
164 | pHook->add( address );
165 |
166 | m_vecInstalledHooks.push_back( std::unique_ptr( pHook ) );
167 |
168 | return pHook;
169 | }
170 |
171 | static void Uninititialize();
172 |
173 | private:
174 | static PVOID AllocateFunctionStub( PVOID origin, PVOID function, int type );
175 |
176 | static LPVOID FindPrevFreeRegion( LPVOID pAddress, LPVOID pMinAddr, DWORD dwAllocationGranularity );
177 | };
178 |
179 |
--------------------------------------------------------------------------------
/ManualReload/Utility/Logger.cpp:
--------------------------------------------------------------------------------
1 | #include "stdafx.h"
2 |
3 | Logger::Logger() : path( Utility::GetModuleName( NULL ) + ".log" ) {
4 | }
5 |
6 | void Logger::Write( const char * format, ... ) const {
7 | char inBuf[MAX_STRING];
8 |
9 | va_list va;
10 |
11 | va_start( va, format );
12 |
13 | vsprintf_s( inBuf, format, va );
14 |
15 | va_end( va );
16 |
17 | std::ofstream ofs( path, std::ios::app );
18 |
19 | ofs << Utility::FormatString( "[%s] [LOG] %s\n",
20 | Utility::GetShortTimeString().c_str(), inBuf );
21 |
22 | ofs.close();
23 | }
24 |
25 | void Logger::Remove() const {
26 | if ( !Utility::FileExists( path ) ) return;
27 |
28 | remove( path.c_str() );
29 | }
30 |
31 | Logger::~Logger() {
32 | //Remove();
33 | }
34 |
--------------------------------------------------------------------------------
/ManualReload/Utility/Logger.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #define LOG(x,...) g_logfile.Write(x, __VA_ARGS__);
4 |
5 | class Logger sealed {
6 | public:
7 | explicit Logger( std::string filename ) : path( filename ) {}
8 | Logger();
9 | void Write( const char * format, ... ) const;
10 | void Remove() const;
11 | ~Logger();
12 | private:
13 | const std::string path;
14 | };
15 |
16 | static Logger g_logfile;
17 |
--------------------------------------------------------------------------------
/ManualReload/Utility/UIText.cpp:
--------------------------------------------------------------------------------
1 | #include "stdafx.h"
2 | #include "UIText.h"
3 |
4 | UIText::UIText( std::string text, float x, float y, float scale, eFont font, eAlignment align, int r, int g, int b, int a, bool pulse ) {
5 | m_text = text;
6 | m_xPos = x;
7 | m_yPos = y;
8 | m_scale = scale;
9 | m_font = font;
10 | m_align = align;
11 | m_shadow = false;
12 | m_outline = true;
13 | m_color.r = r;
14 | m_color.g = g;
15 | m_color.b = b;
16 | m_color.a = a;
17 | m_pulse = pulse;
18 | m_enabled = true;
19 | }
20 |
21 | void UIText::SetText( std::string text ) {
22 | m_text = text;
23 | }
24 |
25 | void UIText::SetScale( float scale ) {
26 | m_scale = scale;
27 | }
28 |
29 | void UIText::SetFont( eFont font ) {
30 | m_font = font;
31 | }
32 |
33 | void UIText::SetColor( color_s color ) {
34 | m_color = color;
35 | }
36 |
37 | void UIText::Draw() {
38 | if ( m_enabled ) {
39 | float x = m_xPos / 1280.0f;
40 | float y = m_yPos / 720.0f;
41 |
42 | if ( m_pulse ) {
43 | bool b = false;
44 |
45 | if ( m_pulseState ) {
46 | if ( b |= m_color.a < 255 ) {
47 | m_color.a = min( 255, m_color.a + 5 );
48 | }
49 |
50 | m_pulseState = b;
51 | }
52 |
53 | else {
54 | if ( b |= m_color.a > 100 ) {
55 | m_color.a = max( 100, m_color.a - 5 );
56 | }
57 |
58 | m_pulseState = !b;
59 | }
60 | }
61 |
62 | if ( m_shadow )
63 | UI::SET_TEXT_DROP_SHADOW();
64 |
65 | if ( m_outline )
66 | UI::SET_TEXT_OUTLINE();
67 |
68 | UI::SET_TEXT_FONT( m_font );
69 |
70 | UI::SET_TEXT_SCALE( m_scale, m_scale );
71 |
72 | UI::SET_TEXT_COLOUR( m_color.r, m_color.g, m_color.b, m_color.a );
73 |
74 | UI::SET_TEXT_JUSTIFICATION( m_align );
75 |
76 | UI::BEGIN_TEXT_COMMAND_DISPLAY_TEXT( textCommandType );
77 |
78 | UI::ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME( m_text.c_str() );
79 |
80 | UI::END_TEXT_COMMAND_DISPLAY_TEXT( x, y );
81 | }
82 | }
83 |
84 | UIText::~UIText()
85 | { }
86 |
--------------------------------------------------------------------------------
/ManualReload/Utility/UIText.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | enum eAlignment {
4 | AlignCenter = 0,
5 | AlignLeft = 1,
6 | AlignRight = 2
7 | };
8 |
9 | class UIText {
10 | bool m_enabled,
11 | m_shadow,
12 | m_outline,
13 | m_pulse,
14 | m_pulseState;
15 | float m_xPos, m_yPos;
16 | eFont m_font;
17 | eAlignment m_align;
18 | float m_scale;
19 | std::string m_text;
20 | color_s m_color;
21 |
22 | const char * textCommandType = "CELL_EMAIL_BCON";
23 | public:
24 | UIText( std::string text, float x, float y, float scale, eFont font, eAlignment align, int r, int g, int b, int a, bool pulse );
25 | ~UIText();
26 | void SetText( std::string text );
27 | void SetScale( float scale );
28 | void SetFont( eFont font );
29 | void SetColor( color_s color );
30 | void Draw();
31 | };
32 |
--------------------------------------------------------------------------------
/ManualReload/Utility/config.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | class CConfig {
5 | public:
6 | CConfig() { }
7 |
8 | explicit CConfig( const char* fileName ) : CConfig( fileName, false ) { }
9 |
10 | explicit CConfig( const char* fileName, bool customPath ) {
11 |
12 | auto path = customPath ? fileName :
13 | Utility::GetWorkingDirectory() + "\\" + fileName;
14 | strcpy_s( filename, &path[0] );
15 | }
16 |
17 | bool getText( char * outBuffer, const char * section, const char * key ) const {
18 | GetPrivateProfileString( TEXT( section ),
19 | TEXT( key ),
20 | NULL,
21 | outBuffer,
22 | sizeof( outBuffer ),
23 | TEXT( filename ) );
24 |
25 | return outBuffer &&
26 | GetLastError() == ERROR_SUCCESS;
27 | }
28 |
29 | void setText( const char * section, const char * key, const char * value ) const {
30 | WritePrivateProfileString( TEXT( section ),
31 | TEXT( key ),
32 | TEXT( value ),
33 | filename );
34 | }
35 |
36 | template
37 | T get( const char * section, const char * key, T defaultValue ) {
38 | T result{};
39 |
40 | TCHAR inBuf[80];
41 |
42 | if ( !getText( inBuf, section, key ) )
43 | return defaultValue;
44 |
45 | std::stringstream sstream;
46 |
47 | sstream.imbue( std::locale( "en-us" ) );
48 |
49 | if ( typeid( T ) == typeid( bool ) ) {
50 | sstream << std::boolalpha << inBuf;
51 | }
52 |
53 | else {
54 | sstream << inBuf;
55 | }
56 |
57 | sstream >> result;
58 |
59 | return result;
60 | }
61 |
62 | template
63 | void set( const char * section, const char * key, T val ) {
64 | std::stringstream sstream;
65 |
66 | sstream.imbue( std::locale( "en-us" ) );
67 |
68 | if ( typeid( T ) == typeid( bool ) ) {
69 | sstream << std::boolalpha << val;
70 | }
71 |
72 | else {
73 | sstream << val;
74 | }
75 |
76 | setText( section, key, &sstream.str()[0] );
77 | }
78 |
79 | TCHAR filename[MAX_PATH];
80 | };
81 |
--------------------------------------------------------------------------------
/ManualReload/Utility/patch.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #define JMPREL_8 0xEB // relative jump short 8bits
4 | #define JMPREL_16_32 0xE9
5 | #define JNZREL_8 0x75 // relative jump short if != 0 8bits
6 | #define JZEREL_16_32 0x0F84 // relative jump short if 0
7 | #define JNZREL_16_32 0x0F85 // relative jump short 16/ 32
8 | #define JBEREL_16_32 0x0F86 // relative jump short if < or ==
9 | #define XOR_16_32_64 0x31
10 | #define NOP 0x90
11 |
12 | #include
13 |
14 | template
15 | struct patch {
16 | patch() : place( nullptr ), active( false ) {
17 | }
18 |
19 | patch( T * pPlace, std::vector const& data ) : active( false ) {
20 | place = pPlace;
21 | newData = data;
22 | originalData = std::vector( place, place + newData.size() );
23 | }
24 |
25 | void install() {
26 | std::copy( newData.begin(), newData.end(), stdext::checked_array_iterator( place, newData.size() ) );
27 |
28 | active = true;
29 | }
30 |
31 | void remove() {
32 | std::copy( originalData.begin(), originalData.end(), stdext::checked_array_iterator( place, originalData.size() ) );
33 |
34 | active = false;
35 | }
36 |
37 | BYTE * place;
38 | std::vector newData, originalData;
39 | bool active;
40 | };
41 |
42 | typedef patch bytepatch_t;
43 |
--------------------------------------------------------------------------------
/ManualReload/Utility/pattern.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | template
6 | class Pattern {
7 | public:
8 | Pattern( const BYTE* bMask, const char* szMask ) : bMask( bMask ), szMask( szMask ) {
9 | bSuccess = findPattern();
10 | }
11 |
12 | T get( const int offset = 0 ) {
13 | return pResult + offset;
14 | }
15 |
16 | bool bSuccess;
17 |
18 | private:
19 | bool findPattern() {
20 | MODULEINFO module = {};
21 |
22 | GetModuleInformation( GetCurrentProcess(), GetModuleHandle( nullptr ), &module, sizeof( MODULEINFO ) );
23 |
24 | auto *address = reinterpret_cast( module.lpBaseOfDll );
25 |
26 | const auto address_end = address + module.SizeOfImage;
27 |
28 | for ( ; address < address_end; address++ ) {
29 | if ( bCompare( static_cast( address ), bMask, szMask ) ) {
30 | pResult = reinterpret_cast( address );
31 | return true;
32 | }
33 | }
34 |
35 | pResult = NULL;
36 | return false;
37 | }
38 |
39 | static bool bCompare( const BYTE* pData, const BYTE* bMask, const char* szMask ) {
40 | for ( ; *szMask; ++szMask, ++pData, ++bMask )
41 | if ( *szMask == 'x' && *pData != *bMask )
42 | return false;
43 | return ( *szMask ) == NULL;
44 | }
45 |
46 | const BYTE *bMask;
47 | const char* szMask;
48 |
49 | T pResult;
50 | };
51 |
52 | class BytePattern : public Pattern {
53 | public:
54 | BytePattern( const BYTE* bMask, const char* szMask ) :
55 | Pattern( bMask, szMask ) {}
56 | };
--------------------------------------------------------------------------------
/ManualReload/dllmain.cpp:
--------------------------------------------------------------------------------
1 | #include "stdafx.h"
2 |
3 | BOOL APIENTRY DllMain( HMODULE hModule,
4 | DWORD ul_reason_for_call,
5 | LPVOID lpReserved
6 | ) {
7 | switch ( ul_reason_for_call ) {
8 | case DLL_PROCESS_ATTACH:
9 | scriptRegister( hModule, main );
10 | break;
11 | case DLL_PROCESS_DETACH:
12 | unload();
13 | scriptUnregister( main );
14 | break;
15 | }
16 | return TRUE;
17 | }
18 |
--------------------------------------------------------------------------------
/ManualReload/main.cpp:
--------------------------------------------------------------------------------
1 | #include "stdafx.h"
2 | #include "Utility/UIText.h"
3 |
4 | using namespace Utility;
5 |
6 | UIText g_reloadText( "Reload", 640.0f, 674.0f, 0.59f, FontCCCLocalized, AlignCenter, 255, 255, 255, 255, true );
7 |
8 | static CConfig g_configFile( "ManualReload.ini" );
9 |
10 | bytepatch_t setWeaponFlagsPatch;
11 |
12 | uintptr_t g_pedWeaponMgrOffset,
13 | g_aimTaskWeaponFlagsOffset,
14 | g_weaponClipComponentOffset,
15 | g_weaponInfoTimeBetweenShotsOffset;
16 |
17 | typedef CWeapon* ( *CPedWeaponMgr__GetCurrentWeapon )( LPVOID );
18 | CPedWeaponMgr__GetCurrentWeapon g_getCurrentWeapon;
19 |
20 | Ped g_playerPed;
21 |
22 | struct CScriptConfig {
23 | bool DiscardClipAmmo;
24 | bool UseReloadText;
25 | bool UseReloadHelpText;
26 | bool UseEmptyChamberSound;
27 | char AltReloadText[256];
28 | } g_userConfig;
29 |
30 | static CallHook * g_set_ammo_in_clip_orig_hook;
31 |
32 | static CallHook * g_reload_check_orig_hook;
33 |
34 | static JmpHook * g_task_on_swap_weap_hook;
35 |
36 | static CallHook * g_task_weapon_flags_orig_hook;
37 |
38 | void readUserConfig() {
39 |
40 | g_userConfig.DiscardClipAmmo = g_configFile.get( "General", "DiscardClipAmmo", true );
41 |
42 | g_userConfig.UseReloadText = g_configFile.get( "General", "UseReloadText", true );
43 |
44 | g_userConfig.UseReloadHelpText = g_configFile.get( "General", "UseReloadHelpText", false );
45 |
46 | g_userConfig.UseEmptyChamberSound = g_configFile.get( "General", "UseEmptyChamberSound", true );
47 |
48 | g_configFile.getText( g_userConfig.AltReloadText, "General", "AltReloadText" );
49 | }
50 |
51 | CWeapon * getCurrentPedWeapon( const Ped ped ) {
52 |
53 | const auto address = getScriptHandleBaseAddress( ped );
54 |
55 | const auto weaponMgr = *reinterpret_cast(
56 | reinterpret_cast( address ) + g_pedWeaponMgrOffset );
57 |
58 | return g_getCurrentWeapon( weaponMgr );
59 | }
60 |
61 | void SetAmmoInClip_Hook( CWeapon* weapon, char bAltAmmmo ) {
62 |
63 | if ( weapon->pInventoryPed->GetOwner() == getScriptHandleBaseAddress( g_playerPed ) &&
64 | g_userConfig.DiscardClipAmmo ) {
65 | weapon->totalAmmo -= weapon->info->clipSize;
66 |
67 | weapon->totalAmmo += weapon->info->clipSize - weapon->ammoInClip;
68 |
69 | weapon->pInventoryPed->SetWeaponAmmo( weapon->info->dwNameHash, weapon->totalAmmo );
70 | }
71 |
72 | g_set_ammo_in_clip_orig_hook->fn( weapon, bAltAmmmo );
73 | }
74 |
75 | bool bReloadingWeapon = false;
76 |
77 | bool CWeapon_CanBeReloaded_Hook( CWeapon * weapon ) {
78 |
79 | if ( weapon->ammoInClip > 0 || weapon->pInventoryPed &&
80 | weapon->pInventoryPed->GetOwner() != getScriptHandleBaseAddress( g_playerPed ) )
81 | return g_reload_check_orig_hook->fn( weapon );
82 |
83 | return bReloadingWeapon;
84 | }
85 |
86 | int CTaskSwapWeapon_UpdateOnSwap_Hook( uint64_t ctask ) {
87 |
88 | const auto ped = *reinterpret_cast( ctask + 0x10 );
89 |
90 | if ( ped == getScriptHandleBaseAddress( g_playerPed ) ) {
91 | const auto weaponMgr = *reinterpret_cast(
92 | reinterpret_cast( ped ) + g_pedWeaponMgrOffset );
93 |
94 | const auto weapon = g_getCurrentWeapon( weaponMgr );
95 |
96 | if ( weapon && weapon->info->dwGroupHash == 2725924767u ) {
97 | *( BYTE* )( ctask + 0xEC ) |= 4;
98 | }
99 | }
100 |
101 | return g_task_on_swap_weap_hook->fn( ctask );
102 | }
103 |
104 | bool CTaskAimGunOnFoot_UpdateWeaponFlags_Hook( uint64_t ctask ) {
105 |
106 | if ( *reinterpret_cast( ctask + 0x10 ) != getScriptHandleBaseAddress( g_playerPed ) )
107 | return g_task_weapon_flags_orig_hook->fn( ctask );
108 |
109 | if ( bReloadingWeapon ) {
110 | *( BYTE* )( ctask + g_aimTaskWeaponFlagsOffset ) |= 0x20;
111 | return true;
112 | }
113 |
114 | return false;
115 | }
116 |
117 | bool bInitialized = false;
118 |
119 | bool initialize( const eGameVersion version ) {
120 |
121 | g_aimTaskWeaponFlagsOffset = version > VER_1_0_877_1_NOSTEAM ? 0x12C : 0x11C;
122 |
123 | g_weaponInfoTimeBetweenShotsOffset = version > VER_1_0_1032_1_NoSteam ? 0x134 : 0x11C;
124 |
125 | #pragma region CanBeReloaded Hook (CTaskGun::UpdateBasic)
126 |
127 | //B8 ? ? ? ? 44 3B E8 74 1C
128 | auto pattern = BytePattern( ( BYTE* )"\xB8\x04\x00\x00\x00\x44\x3B\xE8\x74\x1C", "x????xxxxx" ); //BB 04 00 00 00 44 3B ? 74 17 84
129 |
130 | if ( !pattern.bSuccess ) {
131 | LOG( "Failed to find address #1. Exiting..." );
132 | return false;
133 | }
134 |
135 | g_reload_check_orig_hook = HookManager::SetCall( pattern.get( 44 ), CWeapon_CanBeReloaded_Hook );
136 |
137 | #pragma endregion
138 |
139 | #pragma region CanBeReloaded Hook (CTaskInCover::ShouldReloadWeapon)
140 |
141 | pattern = BytePattern( ( BYTE* )"\x75\x12\x80\x78\x36\x09", "xxxxxx" );
142 |
143 | if ( !pattern.bSuccess ) {
144 | LOG( "Failed to find address #2. Exiting..." );
145 | return false;
146 | }
147 |
148 | g_reload_check_orig_hook->add( pattern.get( 43 ) );
149 |
150 | #pragma endregion
151 |
152 | #pragma region OnSwap Hook (CTaskSwapWeapon::UpdateOnSwap)
153 |
154 | pattern = BytePattern( ( BYTE* )"\x48\x83\xC4\x20\x5B\xE9\x00\x00\x00\x00\x83\xFA\x06\x75\x07", "xxxxxx????xxxxx" );
155 |
156 | if ( !pattern.bSuccess ) {
157 | LOG( "Failed to find address #3. Exiting..." );
158 | return false;
159 | }
160 |
161 | // g_task_on_swap_weap_hook = HookManager::SetJmp((PBYTE)pattern.get(5), CTaskSwapWeapon_UpdateOnSwap_Hook);
162 |
163 | #pragma endregion
164 |
165 | #pragma region WeaponOkCheck
166 |
167 | pattern = BytePattern( ( BYTE* )"\xE8\x00\x00\x00\x00\x8D\x43\xFF\x83\xF8\x03", "x????xxxxxx" );
168 |
169 | if ( !pattern.bSuccess ) {
170 | LOG( "Failed to find address #4. Exiting..." );
171 | return false;
172 | }
173 |
174 | auto address = pattern.get( 1 );
175 |
176 | // nested function CTaskAimGunOnFoot::UpdateWeaponFlags+10
177 | address = *( int32_t* )address + address + 4 + 10;
178 |
179 | // nested function CTaskAimGunOnFoot::IsWeaponReady+66
180 | address = *( int32_t* )address + address + 4 + 66;
181 |
182 | g_pedWeaponMgrOffset = *( int32_t* )address;
183 |
184 | address += 5;
185 |
186 | address = *( int32_t* )address + address + 4;
187 |
188 | g_getCurrentWeapon = ( CPedWeaponMgr__GetCurrentWeapon )address;
189 |
190 | g_task_weapon_flags_orig_hook = HookManager::SetCall( pattern.get(), CTaskAimGunOnFoot_UpdateWeaponFlags_Hook );
191 |
192 | #pragma endregion
193 |
194 | #pragma region SetWeaponFlagsPatch (CTaskAimGunOnFoot)
195 |
196 | pattern = BytePattern( ( BYTE* )"\x8B\x4D\x00\x8D\x41\xFE\x41\x3B", "xx?xxxxx" );
197 |
198 | if ( !pattern.bSuccess ) {
199 | LOG( "Failed to find address #5. Exiting..." );
200 | return false;
201 | }
202 |
203 | // don't disable aiming for an invalid total ammo count..
204 | setWeaponFlagsPatch = bytepatch_t( pattern.get( version > VER_1_0_463_1_NOSTEAM ? 91u : 67u ), std::vector( 8, NOP ) );
205 |
206 | setWeaponFlagsPatch.install();
207 |
208 | #pragma endregion
209 |
210 | if ( version > VER_1_0_463_1_NOSTEAM ) {
211 | #pragma region SetAmmoInClip (CTaskReloadWeapon)
212 |
213 | //48 8D 15 ? ? ? ? 48 8B CF E8 ? ? ? ? 84 C0 74 14 80
214 | pattern = BytePattern( ( BYTE* )"\x48\x8D\x15\x00\x00\x00\x00\x48\x8B\xCF\xE8\x00\x00\x00\x00\x84\xC0\x74\x14\x80", "xxx????xxxx????xxxxx" );
215 | if ( !pattern.bSuccess ) {
216 | LOG( "Failed to find address #6. Exiting..." );
217 | return false;
218 | }
219 |
220 | address = pattern.get( -11 );
221 |
222 | address = *( int32_t* )address + address + 168;
223 |
224 | auto address2 = address + 1;
225 |
226 | address2 = *( int32_t* )address2 + address2 + 4;
227 |
228 | g_set_ammo_in_clip_orig_hook = HookManager::SetCall( address, SetAmmoInClip_Hook );
229 |
230 | g_weaponClipComponentOffset = *( int32_t* )( address2 + 0x2B );
231 |
232 | #pragma endregion
233 |
234 | #pragma region g_persistWeaponAmmoStats toggle
235 |
236 | pattern = BytePattern( ( BYTE* )"\x44\x38\x2D\x00\x00\x00\x00\x74\x1B\x48\x8B\x47\x20", "xxx????xxxxxx" );
237 |
238 | if ( !pattern.bSuccess ) {
239 | LOG( "Failed to find address #7. Exiting..." );
240 | return false;
241 | }
242 |
243 | address = pattern.get( 3 );
244 |
245 | address = *( int32_t* )address + address + 4;
246 |
247 | *( BOOL* )address = 1; // always use ammo inventory
248 |
249 |
250 | #pragma endregion
251 | }
252 |
253 | return bInitialized = true;
254 | }
255 |
256 | int getLanguageTextId() {
257 |
258 | auto uiLanguage = UNK::_GET_UI_LANGUAGE_ID();
259 |
260 | if ( uiLanguage == 11 )
261 | uiLanguage = 4;
262 |
263 | return uiLanguage;
264 | }
265 |
266 | void main() {
267 |
268 | const auto gameVersion = getGameVersion();
269 |
270 | if ( gameVersion == VER_UNK ) return;
271 |
272 | readUserConfig();
273 |
274 | if ( bInitialized || initialize( gameVersion ) ) {
275 |
276 | auto keyStr = std::string( CONTROLS::GET_CONTROL_INSTRUCTIONAL_BUTTON( 0, ControlReload, TRUE ) );
277 |
278 | auto it = keyStr.find_last_of( "_" );
279 |
280 | if ( it != std::string::npos )
281 | it += 1;
282 |
283 | keyStr = keyStr.substr( it );
284 |
285 | const auto reloadText = g_userConfig.AltReloadText[0] == '\0' ?
286 | getConstString( getLanguageTextId(), RELOAD_TEXT ) : g_userConfig.AltReloadText;
287 |
288 | g_reloadText.SetText( FormatString( "%s [%s]", reloadText, keyStr.c_str() ) );
289 |
290 | run();
291 | }
292 | }
293 |
294 | int weaponSoundPlayTime = 0;
295 |
296 | bool weaponSoundTimerEnabled = false;
297 |
298 | inline void playEmptyChamberSound( const int waitTime = 0 ) {
299 |
300 | weaponSoundPlayTime = GAMEPLAY::GET_GAME_TIMER() + waitTime;
301 |
302 | weaponSoundTimerEnabled = true;
303 | }
304 |
305 | inline void updateWeaponSounds( const Weapon weap ) {
306 |
307 | if ( weaponSoundTimerEnabled ) {
308 |
309 | if ( GAMEPLAY::GET_GAME_TIMER() < weaponSoundPlayTime )
310 | return;
311 | // for delayed trigger sounds, make sure we are still firing...
312 | if ( CONTROLS::IS_DISABLED_CONTROL_PRESSED( 0, ControlAttack ) ) {
313 |
314 | auto src = WEAPON::GET_WEAPONTYPE_GROUP( weap ) == 970310034 ?
315 | IDR_DRYFIRE_RIFLE : IDR_DRYFIRE_SMG;
316 |
317 | PlaySoundResource( src, TRUE );
318 | }
319 |
320 | weaponSoundTimerEnabled = false;
321 | }
322 | }
323 |
324 | int lastAmmoInClip = 0;
325 |
326 | void run() {
327 | while ( true ) {
328 |
329 | g_playerPed = PLAYER::PLAYER_PED_ID();
330 |
331 | Hash currentWeapon;
332 |
333 | if ( WEAPON::IS_PED_ARMED( g_playerPed, 4 ) ) {
334 | if ( WEAPON::GET_CURRENT_PED_WEAPON( g_playerPed, ¤tWeapon, TRUE ) ) {
335 |
336 | updateWeaponSounds( currentWeapon );
337 |
338 | int ammoInClip;
339 |
340 | WEAPON::GET_AMMO_IN_CLIP( g_playerPed, currentWeapon, &ammoInClip );
341 |
342 | const int totalAmmo = WEAPON::GET_AMMO_IN_PED_WEAPON( g_playerPed, currentWeapon );
343 |
344 | if ( ammoInClip <= 0 && !PED::IS_PED_RELOADING( g_playerPed ) ) {
345 |
346 | CONTROLS::DISABLE_CONTROL_ACTION( 0, ControlAttack, TRUE );
347 | CONTROLS::DISABLE_CONTROL_ACTION( 0, ControlAttack2, TRUE );
348 | // firing with empty clip
349 | if ( CONTROLS::IS_DISABLED_CONTROL_JUST_PRESSED( 0, ControlAttack ) ) {
350 |
351 | playEmptyChamberSound();
352 | }
353 | // clip just emptied
354 | else if ( lastAmmoInClip == 1 ) {
355 |
356 | if ( CWeapon* weapon = getCurrentPedWeapon( g_playerPed ) ) {
357 |
358 | const float fTimeBetweenShots = *( float* )( ( uintptr_t )weapon->info + g_weaponInfoTimeBetweenShotsOffset );
359 |
360 | playEmptyChamberSound( ( int )( fTimeBetweenShots * 1000.0f ) );
361 | }
362 | }
363 |
364 | // Only show the prompt if we actually have ammo to load
365 | if ( totalAmmo > 0 ) {
366 | if ( g_userConfig.UseReloadHelpText ) {
367 | UI::BEGIN_TEXT_COMMAND_DISPLAY_HELP( "STRING" );
368 | UI::ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME( "Press ~INPUT_RELOAD~ to reload" );
369 | UI::END_TEXT_COMMAND_DISPLAY_HELP( 0, 0, 1, -1 );
370 | }
371 |
372 | else if ( g_userConfig.UseReloadText ) {
373 | g_reloadText.Draw();
374 | }
375 | }
376 | }
377 |
378 | lastAmmoInClip = ammoInClip;
379 |
380 | bReloadingWeapon = CONTROLS::IS_CONTROL_PRESSED( 0, ControlReload ) && totalAmmo - ammoInClip > 0 &&
381 | ammoInClip < WEAPON::GET_MAX_AMMO_IN_CLIP( g_playerPed, currentWeapon, 1 );
382 | }
383 |
384 | else {
385 | // fixes problem where weapons can sometimes fire immediatley after switching.
386 | CONTROLS::DISABLE_CONTROL_ACTION( 0, ControlAttack, TRUE );
387 | CONTROLS::DISABLE_CONTROL_ACTION( 0, ControlAttack2, TRUE );
388 | }
389 | }
390 |
391 | WAIT( 0 );
392 | }
393 | }
394 |
395 | void unload() {
396 | if ( setWeaponFlagsPatch.active )
397 | setWeaponFlagsPatch.remove();
398 |
399 | HookManager::Uninititialize();
400 | }
401 |
--------------------------------------------------------------------------------
/ManualReload/res/weap_dryfire_rifle.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CamxxCore/RealisticReload/cc902a6c0b6ab937a2320faffd331517358baeaa/ManualReload/res/weap_dryfire_rifle.wav
--------------------------------------------------------------------------------
/ManualReload/res/weap_dryfire_smg.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CamxxCore/RealisticReload/cc902a6c0b6ab937a2320faffd331517358baeaa/ManualReload/res/weap_dryfire_smg.wav
--------------------------------------------------------------------------------
/ManualReload/resource.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CamxxCore/RealisticReload/cc902a6c0b6ab937a2320faffd331517358baeaa/ManualReload/resource.h
--------------------------------------------------------------------------------
/ManualReload/stdafx.cpp:
--------------------------------------------------------------------------------
1 | // stdafx.cpp : source file that includes just the standard includes
2 | // ManualReload.pch will be the pre-compiled header
3 | // stdafx.obj will contain the pre-compiled type information
4 |
5 | #include "stdafx.h"
6 |
7 | // TODO: reference any additional headers you need in STDAFX.H
8 | // and not in this file
9 |
--------------------------------------------------------------------------------
/ManualReload/stdafx.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #define __MAJOR_REV__ 1.0
3 | #define __MINOR_REV__ .04
4 |
5 | #define APP_VERSION (__MAJOR_REV__ + __MINOR_REV__)
6 |
7 | #define MAX_STRING 256
8 |
9 | #include
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include "resource.h"
17 |
18 | #include "Utility/General.h"
19 | #include "Utility/Logger.h"
20 | #include "Utility/Hooking.h"
21 | #include "Utility/pattern.h"
22 | #include "Utility/patch.h"
23 | #include "Utility/config.h"
24 |
25 | #include "types.h"
26 | #include "strings.h"
27 |
28 | #pragma region Scripthook Includes
29 |
30 | #include "inc/types.h"
31 | #include "inc/enums.h"
32 | #include "inc/natives.h"
33 | #include "inc/main.h"
34 |
35 | #pragma endregion
36 |
37 | void main();
38 |
39 | void run();
40 |
41 | void unload();
42 |
--------------------------------------------------------------------------------
/ManualReload/strings.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | static LPCTSTR en_US[] {
4 | "Reload"
5 | };
6 |
7 | static LPCTSTR fr_FR[] {
8 | u8"Recharger"
9 | };
10 |
11 | static LPCTSTR de_DE[] {
12 | u8"Nachladen"
13 | };
14 |
15 | static LPCTSTR it_IT[] {
16 | u8"Ricarica"
17 | };
18 |
19 | static LPCTSTR es_ES[] {
20 | u8"Recargar"
21 | };
22 |
23 | static LPCTSTR pt_BR[] {
24 | u8"Recarregar"
25 | };
26 |
27 | static LPCTSTR pl_PL[] {
28 | u8"Przeładowanie"
29 | };
30 |
31 | static LPCTSTR ru_RU[] {
32 | u8"Перезарядка оружия"
33 | };
34 |
35 | static LPCTSTR ko_KR[] {
36 | u8"재장전"
37 | };
38 |
39 | static LPCTSTR zh_CN[] {
40 | u8"補彈"
41 | };
42 |
43 | static LPCTSTR ja_JP[] {
44 | u8"リロード"
45 | };
46 |
47 | enum GlobalTextEntry_t {
48 | RELOAD_TEXT
49 | };
50 |
51 | static LPCTSTR* langtext_array[11] {
52 | en_US,
53 | fr_FR,
54 | de_DE,
55 | it_IT,
56 | es_ES,
57 | pt_BR,
58 | pl_PL,
59 | ru_RU,
60 | ko_KR,
61 | zh_CN,
62 | ja_JP
63 | };
64 |
65 | inline LPCTSTR getConstString( int langugageId, GlobalTextEntry_t textId ) {
66 | return langtext_array[langugageId][textId];
67 | }
68 |
--------------------------------------------------------------------------------
/ManualReload/types.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | typedef struct color_s {
4 | uint8_t r, g, b, a;
5 | } color_t;
6 |
7 | struct CVector {
8 | float x, y, z, w;
9 | };
10 |
11 | class CPedInventory {
12 | public:
13 | virtual ~CPedInventory() = 0;
14 | virtual void SetWeaponAmmo( unsigned int weaponHash, unsigned int ammoCount ) = 0;
15 | virtual void SetWeaponFiredTime( unsigned int weaponHash, int time ) = 0;
16 | virtual void* GetOwner() = 0;
17 | };
18 |
19 | class CWeaponComponentClipInfo {
20 | char pad0[0x38];
21 | public:
22 | virtual ~CWeaponComponentClipInfo() = 0;
23 | int clipSize; //0x40-0x44
24 | };
25 |
26 | class CWeaponComponentClip {
27 | public:
28 | virtual ~CWeaponComponentClip() = 0;
29 | CWeaponComponentClipInfo * info;
30 | };
31 |
32 | struct CWheelWeapon {
33 | unsigned int weaponHash;
34 | unsigned int nameHash;
35 | int suffixHash; //0x8-0xC
36 | int ammoNotInClip; //0xC-0x10
37 | int ammoInClip;
38 | int totalAmmo;
39 | unsigned __int8 bEquiped;
40 | unsigned __int8 bUnk;
41 | unsigned __int16 unkShort1;
42 | };
43 |
44 | enum eWeaponAmmoState : char {
45 | WAS_READY = 0,
46 | WAS_FIRING = 1,
47 | WAS_RELOADING = 2,
48 | WAS_OUT_OF_AMMO = 3
49 | };
50 |
51 | struct CAmmoInfo {
52 | char _0x0000[16];
53 | DWORD dwAmmoNameHash; //0x0010
54 | DWORD dwAmmoModelHash; //0x0014
55 | DWORD dwAmmoAudio; //0x0018
56 | DWORD dwAmmoSlot; //0x001C
57 | __int32 iAmmoMax; //0x0020
58 | __int32 iAmmoMax50; //0x0024
59 | __int32 iAmmoMax100; //0x0028
60 | __int32 iAmmoMaxMP; //0x002C
61 | __int32 iAmmoMax50MP; //0x0030
62 | __int32 iAmmoMax100MP; //0x0034
63 | BYTE bAmmoFlags; //0x0038
64 | char _0x0039[7];
65 | void * projectileInfo; //0x0040
66 |
67 | };//Size=0x0158
68 |
69 | struct CAimingInfo {
70 | DWORD dwNameHash; //0x0000
71 | float fHeadingLimit; //0x0004
72 | float fSweepPitchMin; //0x0008
73 | float fSweepPitchMax; //0x000C
74 | };
75 |
76 | struct CBoneForce {
77 | DWORD dwBoneTag; //0x0000
78 | float fForceFront; //0x0004
79 | float fForceBack; //0x0008
80 | };//Size=0x000C
81 |
82 | struct CBoneForces {
83 | CBoneForce arrBones[6]; //0x0000
84 | };//Size=0x0048
85 |
86 | struct CWeaponInfo {
87 | void ** virtualTable; //0x0-0x8;
88 | void * extensions; //0x8-0x10
89 | DWORD dwNameHash;//0x10-0x14
90 | DWORD dwModelHash; //0x14-0x18
91 | DWORD dwAudioHash;
92 | DWORD dwWeaponSlotHash; //0x1C-0x20
93 | int damageType; //0x20-0x24
94 | DWORD dwExplosionDefault; //0x24-0x28
95 | DWORD dwExplosionHitCar; //0x28-0x2C
96 | DWORD dwExplosionHitTruck; //0x2C-0x30
97 | DWORD dwExplosionHitBike; //0x30-0x34
98 | DWORD dwExplosionHitBoat; //0x34-0x38
99 | DWORD dwExplosionHitPlane; //0x38-0x3C
100 | int iWeaponFireType; //0x3C-0x40
101 | int iWeaponWheelSlot; //0x40-0x44
102 | DWORD dwGroupHash;//0x44-0x48
103 | CAmmoInfo * pAmmoInfo; //0x48-0x50
104 | CAimingInfo * pAimingInfo; //0x50-0x58
105 | int clipSize; //0x58-0x5C
106 | float fAccuracySpread; //0x5C-0x60
107 | float fAccurateModeAccuracyModifier; //0x60-0x64
108 | float fRunAndGunAccuracyModifier; //0x64-0x68
109 | float fRunAndGunAccuracyMaxModifier; //0x68-0x6C
110 | float fRecoilAccuracyMax; //0x6C-0x70
111 | float fRecoilErrorTime; //0x70-0x74
112 | float fRecoilRecoveryRate; //0x74-0x78
113 | float fRecoilAccuracyToAllowHeadshotAI; //0x78-0x7C
114 | float fMinHeadshotDistanceAI; //0x7C-0x80
115 | float fMaxHeadshotDistanceAI; //0x80-0x84
116 | float fHeadshotDamageModifierAI; //0x84-0x88
117 | float fRecoilAccuracyToAllowHeadshotPlayer; //0x88-0x8C
118 | float fMinHeadshotDistancePlayer; //0x8C-0x90
119 | float fMaxHeadshotDistancePlayer; //0x90-0x94
120 | float fHeadShotDamageModifierPlayer; //0x0094
121 | float fDamage; //0x0098
122 | float fDamageTime; //0x009C
123 | float fDamageTimeInVehicle; //0x00A0
124 | float fDamageTimeInVehicleHeadShot; //0x00A4
125 | float fHitLimbsDamageModifier; //0x00A8
126 | float fNetworkHitLimbsDamageModifier; //0x00AC
127 | float fLightlyArmouredDamageModifier; //0x00B0
128 | float fForce; //0x00B4
129 | float fForceUnk; //0x00B8
130 | float fForceHitPed; //0x00BC
131 | float fForceHitVehicle; //0x00C0
132 | float fForceHitFlyingHeli; //0x00C4
133 | CBoneForces * pBoneForces; //0xC8-0xD0
134 | __int16 iBoneForceFrontCount; //0x00D0
135 | __int16 iBoneForceBackCount; //0x00D2
136 | char _0x00D4[4];
137 | float fForceMaxStrengthMult; //0x00D8
138 | float fForceFalloffRangeStart; //0x00DC
139 | float fForceFalloffRangeEnd; //0x00E0
140 | float fForceFalloffMin; //0x00E4
141 | float fProjectileForce; //0x00E8
142 | float fFragImpulse; //0x00EC
143 | float fPenetration; //0x00F0
144 | float fVerticalLaunchAdjustment; //0x00F4
145 | float fDropForwardVelocity; //0x00F8
146 | float fSpeed; //0x00FC
147 | __int32 iBulletsInBatch; //0x0100
148 | float fBatchSpread; //0x0104
149 | float fReloadTimeMP; //0x0108
150 | float fReloadTimeSP; //0x010C
151 | float fVehicleReloadTime; //0x0110
152 | float fAnimReloadRate; //0x0114
153 | __int32 iBulletsPerAnimLoop; //0x0118
154 | float fTimeBetweenShots; //0x011C
155 | float fTimeLeftBetweenShotsWhereShouldFireIsCached; //0x0120
156 | float fSpinUpTime; //0x0124
157 | float fSpinTime; //0x0128
158 | float fSpinDownTime; //0x012C
159 | float fAlternateWaitTime; //0x0130
160 | float fBulletBendingNearRadius; //0x0134
161 | float fBulletBendingFarRadius; //0x0138
162 | float fBulletBendingZoomedRadius; //0x013C
163 | float fFirstPersonBulletBendingNearRadius; //0x0140
164 | float fFirstPersonBulletBendingFarRadius; //0x0144
165 | float fFirstPersonBulletBendingZoomedRadius; //0x0148
166 | char _0x014C[4];
167 | __int32 fFxEffectGroup; //0x0150 (GUESSING - Need to map groups out)
168 | DWORD dwFxFlashFxHash; //0x0154
169 | DWORD dwFxFlashFxAltHash; //0x0158
170 | DWORD dwFxFlashFxFPHash; //0x015C
171 | DWORD dwFxFlashFxAltFPHash; //0x0160
172 | DWORD dwMuzzleSmokeFxHash; //0x0164
173 | DWORD dwMuzzleSmokeFxFPHash; //0x0168
174 | float fFxMuzzleSmokeFxMinLevel; //0x016C
175 | float fFxMuzzleSmokeFxIncPerShot; //0x0170
176 | float fFxMuzzleSmokeFxDecPerSec; //0x0174
177 | char pad2[0x80];
178 | BYTE boolFxGroundDisturbFxEnabled; //0x1F8-0x1F9
179 | char _0x01E9[3]; //0x1F9-0x1FC
180 | float fFxGroundDisturbFxDist; //0x1FC-0x200
181 | DWORD dwFxGroundDisturbFxNameDefaultHash; //0x200-0x204
182 | DWORD dwFxGroundDisturbFxNameSandHash; //0x204-0x208
183 | DWORD dwFxGroundDisturbFxNameDirtHash; //0x208-0x20C
184 | DWORD dwFxGroundDisturbFxNameWaterHash; //0x20C-0x210
185 | char _0x0200[16]; //0x210-0x220
186 | __int32 iInitialRumbleDuration; //0x220-0x224
187 | float fInitialRumbleIntensity; //0x224-0x228
188 | float fInitialRumbleIntensityTrigger; //0x228-0x22C
189 | __int32 iRumbleDuration; //0x22C-0x230
190 | float fRumbleIntensity; //0x230-0x234
191 | float fRumbleIntensityTrigger; //0x234-0x238
192 | float fRumbleDamageIntensity; //0x238-0x23C
193 | __int32 iInitialRumbleDurationFps; //0x23C-0x240
194 | float fInitialRumbleIntensityFps; //0x240-0x244
195 | __int32 iRumbleDurationFps; //0x244-0x248
196 | float fRumbleIntensityFps; //0x248-0x24C
197 | float fNetworkPlayerDamageModifier; //0x24C-0x250
198 | float fNetworkPedDamageModifier; //0x250-0x254
199 | float fNetworkHeadShotPlayerDamageModifier; //0x254-0x258
200 | float fLockOnRange; //0x258-0x25C
201 | float fMaxRange; //0x25C-0x260
202 | char pad6[0x5E0];
203 | void *pWeaponTints; //0x840
204 | char pad7[0x68];
205 | };
206 |
207 | struct CWeapon {
208 | void * virtualTable; //0x0-0x8
209 | void * extensions; //0x8-0x10
210 | CVector offset; //0x10-0x20
211 | CVector position; //0x20-0x30
212 | char pad0[0x10]; //0x30-0x40
213 | CWeaponInfo * info; //0x40-0x48
214 | int activeTime; //0x48-0x4C
215 | int lastFiredTime; //0x4C-0x50
216 | int activeTime2; //0x50-0x54
217 | uint16_t totalAmmo;
218 | uint16_t ammoInClip; //0x56-0x58
219 | __int64 pWeaponObject;
220 | CPedInventory* pInventoryPed; //0x60-=0x68
221 | CWeapon* pUnkWeapon; //self? //0x68-0x70
222 | char pad2[0xB0]; //0x70-0x120
223 | CWeaponComponentClip * clipComponent; //0x120-0x128
224 | char pad3[0x30];
225 | void * pUnk; //0x158-0x160
226 | char pad4[0x60];
227 | eWeaponAmmoState ammoState; //0x1C0
228 | DWORD weaponStateFlags; // 0x80 = silenced
229 | };
230 |
--------------------------------------------------------------------------------