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