├── stdafx.cpp ├── .github └── FUNDING.yml ├── Screenshots └── ss1.png ├── targetver.h ├── stdafx.h ├── includes ├── stdafx.h └── injector │ ├── LICENSE │ ├── utility.hpp │ ├── calling.hpp │ ├── assembly.hpp │ ├── gvm │ ├── translator.hpp │ └── gvm.hpp │ ├── injector.hpp │ └── hooking.hpp ├── NFSC_FixStreakFlares.vcxproj.filters ├── NFSC_FixStreakFlares.sln ├── LICENSE ├── NFSC_FixStreakFlares.filters ├── .gitattributes ├── dllmain.cpp ├── README.md ├── Shaders ├── global.h └── particles.fx ├── NFSC_FixStreakFlares.vcxproj └── .gitignore /stdafx.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://www.paypal.me/xan1242 2 | -------------------------------------------------------------------------------- /Screenshots/ss1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xan1242/nfsc_fixstreakflares/HEAD/Screenshots/ss1.png -------------------------------------------------------------------------------- /targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 11 | // Windows Header Files: 12 | #include 13 | 14 | 15 | 16 | // TODO: reference additional headers your program requires here 17 | -------------------------------------------------------------------------------- /includes/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 11 | // Windows Header Files: 12 | #include 13 | 14 | 15 | 16 | // TODO: reference additional headers your program requires here 17 | -------------------------------------------------------------------------------- /NFSC_FixStreakFlares.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {1c4cd3a3-5ea6-4d3d-adf8-918f4ee14f24} 14 | 15 | 16 | 17 | 18 | Shaders 19 | 20 | 21 | Shaders 22 | 23 | 24 | -------------------------------------------------------------------------------- /includes/injector/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012-2014 LINK/2012 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | 22 | -------------------------------------------------------------------------------- /NFSC_FixStreakFlares.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}") = "NFSC_FixStreakFlares", "NFSC_FixStreakFlares.vcxproj", "{3C558AD9-5F9C-4A14-8F07-800F46C132C7}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x86 = Debug|x86 11 | Release|x86 = Release|x86 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {3C558AD9-5F9C-4A14-8F07-800F46C132C7}.Debug|x86.ActiveCfg = Debug|Win32 15 | {3C558AD9-5F9C-4A14-8F07-800F46C132C7}.Debug|x86.Build.0 = Debug|Win32 16 | {3C558AD9-5F9C-4A14-8F07-800F46C132C7}.Release|x86.ActiveCfg = Release|Win32 17 | {3C558AD9-5F9C-4A14-8F07-800F46C132C7}.Release|x86.Build.0 = Release|Win32 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Lovro Pleše 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NFSC_FixStreakFlares.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | 26 | 27 | Source Files 28 | 29 | 30 | Source Files 31 | 32 | 33 | -------------------------------------------------------------------------------- /includes/injector/utility.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Injectors - Utility / Helpers 3 | * 4 | * Copyright (C) 2014 LINK/2012 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 22 | * 3. This notice may not be removed or altered from any source 23 | * distribution. 24 | * 25 | */ 26 | #pragma once 27 | 28 | namespace injector 29 | { 30 | template 31 | T return_value() 32 | { 33 | return value; 34 | } 35 | 36 | template 37 | void* force_ptr(const T& fun) 38 | { 39 | auto ptr = fun; 40 | return *(void**)&ptr; 41 | } 42 | 43 | 44 | // Helper structure to help calling back what was there before a hook 45 | // e.g. hb.fun = MakeCALL(0x0, raw_ptr(my_hook)); 46 | template 47 | struct hook_back 48 | { 49 | typedef FuncType func_type; 50 | 51 | func_type fun; 52 | 53 | hook_back() : fun(nullptr) 54 | {} 55 | }; 56 | }; 57 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /dllmain.cpp: -------------------------------------------------------------------------------- 1 | // NFS Carbon - streak flare restore code 2 | 3 | #include "stdafx.h" 4 | #include "stdio.h" 5 | #include 6 | #include 7 | #include "includes\injector\injector.hpp" 8 | 9 | typedef struct D3DXVECTOR4 { 10 | FLOAT x; 11 | FLOAT y; 12 | FLOAT z; 13 | FLOAT w; 14 | } D3DXVECTOR4, *LPD3DXVECTOR4; 15 | 16 | //int ParticlesShaderObjAddr = 0x00B42FF0; 17 | int ParticlesShaderObj = 0; 18 | int VisualTreatmentPlatAddr = 0x00AB0B80; 19 | 20 | void __declspec(naked) FXLEffect_SetVectorFA(int ShaderID, D3DXVECTOR4* pVector) 21 | { 22 | _asm 23 | { 24 | mov edx, [esp + 8] 25 | mov eax, [ecx + 0x44] 26 | push esi 27 | mov esi, [eax] 28 | push edx 29 | mov edx, [esp + 0xC] 30 | lea edx, [edx + edx * 4] 31 | mov ecx, [ecx + edx * 8 + 0x70] 32 | push ecx 33 | push eax 34 | call dword ptr[esi + 0x88] 35 | pop esi 36 | retn 8 37 | } 38 | } 39 | 40 | // set the object address for rain shader here... 41 | #define SHADER_OBJECT_ADDRESS 0x00B42FF0 42 | int sub_73EAE0 = 0x73EAE0; 43 | int sub_710CC0 = 0x710CC0; 44 | int eEffect_SetCurrentPass = 0x00723600; 45 | char* techName = "raindrop"; // custom effect technique added to the shader! 46 | void __declspec(naked) SetRainShader() 47 | { 48 | _asm 49 | { 50 | mov eax, [esp + 4] 51 | push esi 52 | mov byte ptr[ecx], 1 53 | mov ecx, [eax + 8] 54 | push edi 55 | push ecx 56 | push 0 57 | push techName 58 | push 0 59 | mov ecx, ds: [SHADER_OBJECT_ADDRESS] 60 | call eEffect_SetCurrentPass 61 | call sub_73EAE0 62 | mov ecx, ds:[SHADER_OBJECT_ADDRESS] 63 | mov edx, [ecx] 64 | add esp, 4 65 | push 0 66 | push eax 67 | mov eax, [esp + 0x1C] 68 | push eax 69 | call dword ptr[edx + 8] 70 | mov esi, [esp + 0x10] 71 | mov ecx, ds: [SHADER_OBJECT_ADDRESS] 72 | mov edi, [esi] 73 | mov eax, [ecx + 0x44] 74 | mov edi, [edi + 0x10] 75 | mov ecx, [ecx + 0x660] 76 | mov edx, [eax] 77 | push edi 78 | push ecx 79 | push eax 80 | call dword ptr[edx + 0x0D0] 81 | lea edx, [esp + 0xC] 82 | push edx 83 | mov ecx, 0xAAF558 84 | mov ds : [0xAB0AB4] , esi 85 | call sub_710CC0 86 | pop edi 87 | mov al, 1 88 | pop esi 89 | retn 0x10 90 | } 91 | } 92 | 93 | 94 | void __cdecl FXLEffect_SetVectorFA_Hook(int ShaderID, D3DXVECTOR4* pVector) 95 | { 96 | // Vector in question is cvBaseAlphaRef 97 | pVector->y = *(float*)((*(int*)VisualTreatmentPlatAddr) + 0xB4); // cameraSpeed 98 | pVector->w = *(float*)((*(int*)VisualTreatmentPlatAddr) + 0x34); // nosAmount 99 | pVector->x = *(float*)((*(int*)VisualTreatmentPlatAddr) + 0xC0); // camera x direction - tested by Aero_ - thanks for allowing me to pick your brains! 100 | //pVector->x = 0; 101 | // z we keep intact... that's the var that switches between animation frames 102 | 103 | ParticlesShaderObj = *(int*)0x00B42FF0; 104 | _asm mov ecx, ParticlesShaderObj 105 | return FXLEffect_SetVectorFA(ShaderID, pVector); 106 | } 107 | 108 | int Init() 109 | { 110 | injector::MakeCALL(0x00749FF0, FXLEffect_SetVectorFA_Hook, true); 111 | //injector::MakeCALL(0x00749F1C, FXLEffect_SetVectorFA_Hook, true); // causes flicker in motion blur 112 | injector::MakeCALL(0x007C5882, SetRainShader, true); 113 | 114 | return 0; 115 | } 116 | 117 | BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD reason, LPVOID /*lpReserved*/) 118 | { 119 | if (reason == DLL_PROCESS_ATTACH) 120 | { 121 | Init(); 122 | } 123 | return TRUE; 124 | } 125 | 126 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NFS Carbon PC - Streak Flare fixer 2 | 3 | A fixed particle shader and a complementary library to repair the game code for the PC version of NFS Carbon. 4 | 5 | ## What does this do? 6 | 7 | This mod fixes the missing light streaks/trails from the light flares (namely light poles and traffic lights) on the PC version, giving a better Sense of Speed. 8 | 9 | ## How does it work (and why is it broken)? 10 | 11 | There are 2 parts to this fix: shader fixes and code fixes 12 | 13 | ### Shader 14 | 15 | For shaders, I had no choice but to resort to the Xbox 360 shaders from NFS Pro Street. Since the render pipelines between Carbon and Pro Street hadn't changed much at all for particle effects, the shaders worked almost right out of the box. 16 | 17 | However, as I was adapting the 360 shaders, it was necessary to assign the filters and textures manually to the samplers. This was completely omitted from the 360 shader, causing it to not render at all by default. After adding it, particles worked with the 360 shader! (also needed to remap `cmWorldViewProj` to `WorldViewProj`) 18 | 19 | Except for the `streak_flares` technique, that did not work. 20 | 21 | `streak_flares` required its positions to be capped to the world position. Without this, they do not appear on screen. (`world_position(pv)`) 22 | 23 | Secondly, what also was very necessary were some code fixes to repair the `cvBaseAlphaRef` variable. It was stubbed out on PC, which caused it not to draw (specifically `cvBaseAlphaRef.x` was stuck to 1.0, causing the vertex color to be transparent). This I'll explain in the next part. 24 | 25 | And lastly, with this new shader came new bugs - the rain needed a completely custom technique because the fog rendering was affecting it too, causing it to be very faint and hard to see. Disabling the fog on it and scaling/multiplying the color up by 2 fixed the problem 26 | 27 | ### Code 28 | 29 | Code fixes mostly revolve around restoring the cvBaseAlphaRef variable, but also fixing the rain streaks. 30 | 31 | The plugin code (which you can see in dllmain.cpp) contains a hook to `FXLEffect_SetVectorFA` function which, as its name says, it sets a vector of an effect in memory. (This sets up and calls the `ID3DXBaseEffect::SetVector` function) 32 | 33 | The hook basically takes the appropriate values from the game memory (in `pVisualTreatmentPlat`) and writes them to the vector. 34 | 35 | This includes the speed of the camera, amount of NOS and the camera's X direction. 36 | 37 | As for the rain, included is a hook to a function which controls some parameters in the shader during `Rain::Render`. By setting a custom technique in the shader called "raindrop" we can control the shader only on the rain and not the other particles. 38 | 39 | This is done by calling `eEffect::SetCurrentPass`. 40 | 41 | ### What went wrong during PC porting? 42 | 43 | It's hard to tell. 44 | 45 | It might've been time constraints, it might've been poor development tools, it might've been poor PC hardware at the time. 46 | 47 | My best guess is that Black Box didn't have enough time to optimize this part of the code to work on a wide variety of hardware because they were targeting support for a minimum of pixel shader model 1.1 whereas on consoles they didn't have that limitation. 48 | 49 | We can safely assume that Black Box was mostly developing with NVIDIA GPUs on their PC dev machines as the game supports NVPerfHud natively. 50 | 51 | ## What does this look like and where can I get it? 52 | 53 | You can download a release package in the Releases tab and try it for yourself. 54 | 55 | 56 | 57 | The release package is compiled with VS2022, so you'll need the redistributables installed. 58 | 59 | As for what it looks like, if you've seen the 360 version, you've seen the PC version too. 60 | 61 | ![Streakflares](Screenshots/ss1.png) 62 | -------------------------------------------------------------------------------- /Shaders/global.h: -------------------------------------------------------------------------------- 1 | #ifndef GLOBAL_H 2 | #define GLOBAL_H 3 | 4 | //#include "registermap.h" 5 | 6 | // Defined out for the Xbox360 - only needed on the PC 7 | #define DECLARE_TEXTURE(textured) shared texture textured : textured; 8 | #define ASSIGN_TEXTURE(textured) Texture = ; 9 | #define DECLARE_MINFILTER(default_filter) MINFILTER = default_filter; 10 | #define DECLARE_MAGFILTER(default_filter) MAGFILTER = default_filter; 11 | #define DECLARE_MIPFILTER(default_filter) MIPFILTER = default_filter; 12 | #define COMPILETIME_BOOL bool 13 | #define cmWorldViewProj WorldViewProj 14 | //#define DIFFUSE_SAMPLER DIFFUSEMAP_SAMPLER 15 | 16 | ///////////////////////////////////////////////////////////////////////////////////////// 17 | float4x4 cmWorldViewProj : WorldViewProj; //WORLDVIEWPROJECTION ; 18 | //float4x4 WorldViewProj : REG_cmWorldViewProj; //WORLDVIEWPROJECTION ; 19 | float4 cvScreenOffset : cvScreenOffset; //SCREENOFFSET; 20 | float4 cvVertexPowerBrightness : cvVertexPowerBrightness; 21 | 22 | float4 world_position( float4 screen_pos ) 23 | { 24 | float4 p = mul(screen_pos, cmWorldViewProj); 25 | p.xy += cvScreenOffset.xy * p.w; 26 | return p; 27 | } 28 | 29 | float4 screen_position( float4 screen_pos ) 30 | { 31 | screen_pos.xy += cvScreenOffset.xy; 32 | return screen_pos; 33 | } 34 | 35 | float4 CalcVertexColour(float4 colour) 36 | { 37 | float4 result = pow(colour, cvVertexPowerBrightness.x) * cvVertexPowerBrightness.y; 38 | result.w = colour.w; 39 | return result; 40 | } 41 | 42 | float3 ScaleHeadLightIntensity(float3 colour) 43 | { 44 | float3 result = colour * cvVertexPowerBrightness.z; 45 | return result; 46 | } 47 | 48 | 49 | 50 | ///////////////////////////////////////////////////////////////////////////////////////// 51 | // HDR Colour Space compression 52 | // 53 | // Convert to a log or psudeo-log colour space to save high dynamic range data 54 | ///////////////////////////////////////////////////////////////////////////////////////// 55 | #define kCompressCoeff ( 1.0f ) 56 | float3 CompressColourSpace(float3 colour) 57 | { 58 | return colour / (kCompressCoeff+colour); 59 | } 60 | 61 | float3 DeCompressColourSpace(float3 colour) 62 | { 63 | float3 clr = max( 0.01, kCompressCoeff-colour ); 64 | return colour / clr; 65 | } 66 | 67 | 68 | ///////////////////////////////////////////////////////////////////////////////////////// 69 | // RGBE8 Encoding/Decoding 70 | // The RGBE8 format stores a mantissa per color channel and a shared exponent 71 | // stored in alpha. Since the exponent is shared, it's computed based on the 72 | // highest intensity color component. The resulting color is RGB * 2^Alpha, 73 | // which scales the data across a logarithmic scale. 74 | ///////////////////////////////////////////////////////////////////////////////////////// 75 | 76 | float4 EncodeRGBE8( in float3 rgb ) 77 | { 78 | float4 vEncoded; 79 | 80 | // Determine the largest color component 81 | float maxComponent = max( max(rgb.r, rgb.g), rgb.b ); 82 | 83 | // Round to the nearest integer exponent 84 | float fExp = ceil( log2(maxComponent) ); 85 | 86 | // Divide the components by the shared exponent 87 | vEncoded.rgb = rgb / exp2(fExp); 88 | 89 | // Store the shared exponent in the alpha channel 90 | vEncoded.a = (fExp + 128) / 255; 91 | 92 | return vEncoded; 93 | } 94 | 95 | ///////////////////////////////////////////////////////////////////////////////////////// 96 | 97 | float3 DecodeRGBE8( in float4 rgbe ) 98 | { 99 | float3 vDecoded; 100 | 101 | // Retrieve the shared exponent 102 | float fExp = rgbe.a * 255 - 128; 103 | 104 | // Multiply through the color components 105 | vDecoded = rgbe.rgb * exp2(fExp); 106 | 107 | return vDecoded; 108 | } 109 | 110 | ///////////////////////////////////////////////////////////////////////////////////////// 111 | #endif 112 | -------------------------------------------------------------------------------- /includes/injector/calling.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Injectors - Function Calls Using Variadic Templates 3 | * 4 | * Copyright (C) 2014 LINK/2012 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 22 | * 3. This notice may not be removed or altered from any source 23 | * distribution. 24 | * 25 | */ 26 | #pragma once 27 | #include "injector.hpp" 28 | #include 29 | 30 | #if __cplusplus >= 201103L || _MSC_VER >= 1800 // MSVC 2013 31 | #else 32 | #error "This feature is not supported on this compiler" 33 | #endif 34 | 35 | namespace injector 36 | { 37 | template 38 | struct cstd; 39 | 40 | template 41 | struct cstd 42 | { 43 | // Call function at @p returning @Ret with args @Args 44 | static Ret call(memory_pointer_tr p, Args... a) 45 | { 46 | auto fn = (Ret(*)(Args...)) p.get(); 47 | return fn(std::forward(a)...); 48 | } 49 | 50 | template // Uses lazy pointer 51 | static Ret call(Args... a) 52 | { 53 | return call(lazy_pointer::get(), std::forward(a)...); 54 | } 55 | }; 56 | 57 | template 58 | struct stdcall; 59 | 60 | template 61 | struct stdcall 62 | { 63 | // Call function at @p returning @Ret with args @Args 64 | static Ret call(memory_pointer_tr p, Args... a) 65 | { 66 | auto fn = (Ret(__stdcall *)(Args...)) p.get(); 67 | return fn(std::forward(a)...); 68 | } 69 | 70 | template // Uses lazy pointer 71 | static Ret call(Args... a) 72 | { 73 | return call(lazy_pointer::get(), std::forward(a)...); 74 | } 75 | }; 76 | 77 | template 78 | struct fastcall; 79 | 80 | template 81 | struct fastcall 82 | { 83 | // Call function at @p returning @Ret with args @Args 84 | static Ret call(memory_pointer_tr p, Args... a) 85 | { 86 | auto fn = (Ret(__fastcall *)(Args...)) p.get();; 87 | return fn(std::forward(a)...); 88 | } 89 | 90 | template // Uses lazy pointer 91 | static Ret call(Args... a) 92 | { 93 | return call(lazy_pointer::get(), std::forward(a)...); 94 | } 95 | }; 96 | 97 | template 98 | struct thiscall; 99 | 100 | template 101 | struct thiscall 102 | { 103 | // Call function at @p returning @Ret with args @Args 104 | static Ret call(memory_pointer_tr p, Args... a) 105 | { 106 | auto fn = (Ret(__thiscall *)(Args...)) p.get(); 107 | return fn(std::forward(a)...); 108 | } 109 | 110 | // Call function at the index @i from the vtable of the object @a[0] 111 | template 112 | static Ret vtbl(Args... a) 113 | { 114 | auto obj = raw_ptr(std::get<0>(std::forward_as_tuple(a...))); 115 | auto p = raw_ptr( (*obj.template get()) [i] ); 116 | return call(p, std::forward(a)...); 117 | } 118 | 119 | template // Uses lazy pointer 120 | static Ret call(Args... a) 121 | { 122 | return call(lazy_pointer::get(), std::forward(a)...); 123 | } 124 | }; 125 | } 126 | 127 | -------------------------------------------------------------------------------- /NFSC_FixStreakFlares.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {3C558AD9-5F9C-4A14-8F07-800F46C132C7} 15 | Win32Proj 16 | NFSC_FixStreakFlares 17 | NFSC_FixStreakFlares 18 | 10.0 19 | 20 | 21 | 22 | DynamicLibrary 23 | true 24 | v143 25 | MultiByte 26 | 27 | 28 | DynamicLibrary 29 | false 30 | v143 31 | true 32 | MultiByte 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | true 46 | .asi 47 | $(ProjectName) 48 | 49 | 50 | false 51 | .asi 52 | $(ProjectName) 53 | 54 | 55 | 56 | Use 57 | Level3 58 | Disabled 59 | WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 60 | 61 | 62 | Windows 63 | true 64 | 65 | 66 | 67 | 68 | Level4 69 | Use 70 | MaxSpeed 71 | true 72 | true 73 | WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 74 | MultiThreadedDLL 75 | 76 | 77 | Windows 78 | true 79 | true 80 | true 81 | 82 | 83 | 84 | 85 | true 86 | 87 | 88 | 89 | 90 | 91 | 92 | false 93 | 94 | 95 | false 96 | 97 | 98 | 99 | 100 | Create 101 | Create 102 | 103 | 104 | 105 | 106 | Document 107 | true 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /includes/injector/assembly.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Injectors - Useful Assembly Stuff 3 | * 4 | * Copyright (C) 2012-2014 LINK/2012 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 22 | * 3. This notice may not be removed or altered from any source 23 | * distribution. 24 | * 25 | */ 26 | #pragma once 27 | 28 | // This header is very restrict about compiler and architecture 29 | #ifndef _MSC_VER // MSVC is much more flexible when we're talking about inline assembly 30 | #error Cannot use this header in another compiler other than MSVC 31 | #endif 32 | #ifndef _M_IX86 33 | #error Supported only in x86 34 | #endif 35 | 36 | // 37 | #include 38 | #include "injector.hpp" 39 | 40 | namespace injector 41 | { 42 | struct reg_pack 43 | { 44 | // The ordering is very important, don't change 45 | // The first field is the last to be pushed and first to be poped 46 | 47 | // PUSHFD / POPFD 48 | uint32_t ef; 49 | 50 | // PUSHAD/POPAD -- must be the lastest fields (because of esp) 51 | union 52 | { 53 | uint32_t arr[8]; 54 | struct { uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; }; 55 | }; 56 | 57 | enum reg_name { 58 | reg_edi, reg_esi, reg_ebp, reg_esp, reg_ebx, reg_edx, reg_ecx, reg_eax 59 | }; 60 | 61 | enum ef_flag { 62 | carry_flag = 0, parity_flag = 2, adjust_flag = 4, zero_flag = 6, sign_flag = 7, 63 | direction_flag = 10, overflow_flag = 11 64 | }; 65 | 66 | uint32_t& operator[](size_t i) 67 | { return this->arr[i]; } 68 | const uint32_t& operator[](size_t i) const 69 | { return this->arr[i]; } 70 | 71 | template // bit starts from 0, use ef_flag enum 72 | bool flag() 73 | { 74 | return (this->ef & (1 << bit)) != 0; 75 | } 76 | 77 | bool jnb() 78 | { 79 | return flag() == false; 80 | } 81 | }; 82 | 83 | // Lowest level stuff (actual assembly) goes on the following namespace 84 | // PRIVATE! Skip this, not interesting for you. 85 | namespace injector_asm 86 | { 87 | // Wrapper functor, so the assembly can use some templating 88 | template 89 | struct wrapper 90 | { 91 | static void call(reg_pack* regs) 92 | { 93 | T fun; fun(*regs); 94 | } 95 | }; 96 | 97 | // Constructs a reg_pack and calls the wrapper functor 98 | template // where W is of type wrapper 99 | inline void __declspec(naked) make_reg_pack_and_call() 100 | { 101 | _asm 102 | { 103 | // Construct the reg_pack structure on the stack 104 | pushad // Pushes general purposes registers to reg_pack 105 | add [esp+12], 4 // Add 4 to reg_pack::esp 'cuz of our return pointer, let it be as before this func is called 106 | pushfd // Pushes EFLAGS to reg_pack 107 | 108 | // Call wrapper sending reg_pack as parameter 109 | push esp 110 | call W::call 111 | add esp, 4 112 | 113 | // Destructs the reg_pack from the stack 114 | sub [esp+12+4], 4 // Fix reg_pack::esp before popping it (doesn't make a difference though) (+4 because eflags) 115 | popfd // Warning: Do not use any instruction that changes EFLAGS after this (-> sub affects EF!! <-) 116 | popad 117 | 118 | // Back to normal flow 119 | ret 120 | } 121 | } 122 | }; 123 | 124 | 125 | /* 126 | * MakeInline 127 | * Makes inline assembly (but not assembly, an actual functor of type FuncT) at address 128 | */ 129 | template 130 | void MakeInline(memory_pointer_tr at) 131 | { 132 | typedef injector_asm::wrapper functor; 133 | if(false) functor::call(nullptr); // To instantiate the template, if not done _asm will fail 134 | MakeCALL(at, injector_asm::make_reg_pack_and_call); 135 | } 136 | 137 | /* 138 | * MakeInline 139 | * Same as above, but it NOPs everything between at and end (exclusive), then performs MakeInline 140 | */ 141 | template 142 | void MakeInline(memory_pointer_tr at, memory_pointer_tr end) 143 | { 144 | MakeRangedNOP(at, end); 145 | MakeInline(at); 146 | } 147 | 148 | /* 149 | * MakeInline 150 | * Same as above, but (at,end) are template parameters. 151 | * On this case the functor can be passed as argument since there will be one func instance for each at,end not just for each FuncT 152 | */ 153 | template 154 | void MakeInline(FuncT func) 155 | { 156 | static std::unique_ptr static_func; 157 | static_func.reset(new FuncT(std::move(func))); 158 | 159 | // Encapsulates the call to static_func 160 | struct Caps 161 | { 162 | void operator()(reg_pack& regs) 163 | { (*static_func)(regs); } 164 | }; 165 | 166 | // Does the actual MakeInline 167 | return MakeInline(lazy_pointer::get(), lazy_pointer::get()); 168 | } 169 | 170 | /* 171 | * MakeInline 172 | * Same as above, but (end) is calculated by the length of a call instruction 173 | */ 174 | template 175 | void MakeInline(FuncT func) 176 | { 177 | return MakeInline(func); 178 | } 179 | }; 180 | -------------------------------------------------------------------------------- /includes/injector/gvm/translator.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Injectors - Address Translation Management 3 | * 4 | * Copyright (C) 2014 LINK/2012 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 22 | * 3. This notice may not be removed or altered from any source 23 | * distribution. 24 | * 25 | */ 26 | #pragma once 27 | 28 | #if !defined(INJECTOR_GVM_HAS_TRANSLATOR) 29 | #error Missing INJECTOR_GVM_HAS_TRANSLATOR on compiler definitions 30 | #endif 31 | 32 | /* 33 | * This is a quick solution for address translations if you're too lazy to implement a proper address_manager::translator by yourself 34 | * So, just call address_translator_manager::singleton().translate(p) from your address_manager::translator and that's it. 35 | * It'll translate addresses based on 'address_translator' objects, when one gets constructed it turns into a possible translator. 36 | * At the constructor of your derived 'address_translator' make the map object to have [addr_to_translate] = translated_addr; 37 | * There's also the virtual method 'fallback' that will get called when the translation wasn't possible, you can do some fallback stuff here 38 | * (such as return the pointer as is or output a error message) 39 | */ 40 | 41 | #include "../injector.hpp" 42 | #include 43 | #include 44 | #include 45 | 46 | namespace injector 47 | { 48 | /* 49 | * address_translator 50 | * Base for an address translator 51 | */ 52 | class address_translator 53 | { 54 | private: 55 | bool enabled; 56 | void add(); 57 | void remove(); 58 | 59 | protected: 60 | friend class address_translator_manager; 61 | std::map map; 62 | 63 | public: 64 | address_translator() : enabled(true) 65 | { 66 | // Must have bounds filled with min ptr and max ptr to have search working properly 67 | map.insert(std::make_pair(raw_ptr(0x00000000u), raw_ptr(0x00000000u))); 68 | map.insert(std::make_pair(raw_ptr(0xffffffffu), raw_ptr(0xffffffffu))); 69 | add(); 70 | } 71 | 72 | ~address_translator() 73 | { 74 | remove(); 75 | } 76 | 77 | virtual void* fallback(void*) const 78 | { 79 | return nullptr; 80 | } 81 | 82 | 83 | // Enables or disables this translator 84 | void enable(bool enable_it) 85 | { 86 | if(enable_it) this->enable(); 87 | else this->disable(); 88 | } 89 | 90 | // Enables this translator 91 | void enable() 92 | { 93 | this->enabled = true; 94 | } 95 | 96 | // Disables this translator 97 | void disable() 98 | { 99 | this->enabled = false; 100 | } 101 | 102 | // Checks if this translator is enabled 103 | bool is_enabled() const 104 | { 105 | return enabled; 106 | } 107 | }; 108 | 109 | /* 110 | * address_translator_manager 111 | * Manages the address_translator objects 112 | */ 113 | class address_translator_manager 114 | { 115 | protected: 116 | friend class address_manager; 117 | friend class address_translator; 118 | 119 | std::list translators; 120 | 121 | void add(const address_translator& t) 122 | { 123 | translators.push_front(&t); 124 | } 125 | 126 | void remove(const address_translator& t) 127 | { 128 | translators.remove(&t); 129 | } 130 | 131 | public: 132 | // Translates the address p 133 | void* translator(void* p); 134 | 135 | // Singleton object 136 | static address_translator_manager& singleton() 137 | { 138 | static address_translator_manager mgr; 139 | return mgr; 140 | } 141 | }; 142 | 143 | 144 | 145 | inline void* address_translator_manager::translator(void* p_) 146 | { 147 | static const size_t max_ptr_dist = 7; 148 | 149 | // Tries to find an address in a translator map 150 | auto try_map = [](const std::map& map, memory_pointer_raw p) -> memory_pointer_raw 151 | { 152 | memory_pointer_raw result = nullptr; 153 | 154 | // Find first element in the map that is greater than or equal to p 155 | auto it = map.lower_bound(p); 156 | if(it != map.end()) 157 | { 158 | // If it's not exactly the address, get back one position on the table 159 | if(it->first != p) --it; 160 | 161 | auto diff = (p - it->first).as_int(); // What's the difference between p and that address? 162 | if(diff <= max_ptr_dist) // Could we live with this difference in hands? 163 | result = it->second + raw_ptr(diff); // Yes, we can! 164 | } 165 | 166 | return result; 167 | }; 168 | 169 | 170 | // 171 | memory_pointer_raw result = nullptr; 172 | 173 | // Try to find translation for this pointer 174 | auto& mgr = address_translator_manager::singleton().translators; 175 | for(auto it = mgr.begin(); result == nullptr && it != mgr.end(); ++it) 176 | { 177 | auto& t = **it; 178 | if(t.is_enabled()) result = try_map(t.map, p_); 179 | } 180 | 181 | // If we couldn't translate the address, notify and try to fallback 182 | if(result.is_null()) 183 | { 184 | for(auto it = mgr.begin(); result == nullptr && it != mgr.end(); ++it) 185 | { 186 | auto& t = **it; 187 | if(t.is_enabled()) result = t.fallback(p_); 188 | } 189 | } 190 | 191 | return result.get(); 192 | } 193 | 194 | inline void address_translator::add() 195 | { 196 | address_translator_manager::singleton().add(*this); 197 | } 198 | 199 | inline void address_translator::remove() 200 | { 201 | address_translator_manager::singleton().remove(*this); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /includes/injector/gvm/gvm.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Injectors - Base Header 3 | * 4 | * Copyright (C) 2012-2014 LINK/2012 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 22 | * 3. This notice may not be removed or altered from any source 23 | * distribution. 24 | * 25 | */ 26 | #pragma once 27 | #include 28 | #include 29 | #include 30 | 31 | namespace injector 32 | { 33 | 34 | #if 1 // GVM and Address Translator, Not very interesting for the users, so skip reading those... 35 | 36 | /* 37 | * game_version_manager 38 | * Detects the game, the game version and the game region 39 | * This assumes the executable is decrypted, so, Silent's ASI Loader is recommended. 40 | */ 41 | #ifndef INJECTOR_OWN_GVM 42 | #ifndef INJECTOR_GVM_DUMMY 43 | class game_version_manager 44 | { 45 | public: 46 | // Set this if you would like that MessagesBox contain PluginName as caption 47 | const char* PluginName; 48 | 49 | private: 50 | char game, region, major, minor, majorRevision, minorRevision, cracker, steam; 51 | 52 | public: 53 | game_version_manager() 54 | { 55 | #ifdef INJECTOR_GVM_PLUGIN_NAME 56 | PluginName = INJECTOR_GVM_PLUGIN_NAME; 57 | #else 58 | PluginName = "LODLights.asi"; 59 | #endif 60 | 61 | this->Clear(); 62 | } 63 | 64 | 65 | // Clear any information about game version 66 | void Clear() 67 | { 68 | game = region = major = minor = cracker = steam = 0; 69 | } 70 | 71 | // Checks if I don't know the game we are attached to 72 | bool IsUnknown() { return game == 0; } 73 | // Checks if this is the steam version 74 | bool IsSteam() { return steam != 0; } 75 | // Gets the game we are attached to (0, '3', 'V', 'S', 'I', 'E') 76 | char GetGame() { return game; } 77 | // Gets the region from the game we are attached to (0, 'U', 'E'); 78 | char GetRegion() { return region; } 79 | // Get major and minor version of the game (e.g. [major = 1, minor = 0] = 1.0) 80 | int GetMajorVersion() { return major; } 81 | int GetMinorVersion() { return minor; } 82 | int GetMajorRevisionVersion() { return majorRevision; } 83 | int GetMinorRevisionVersion() { return minorRevision; } 84 | 85 | bool IsHoodlum() { return cracker == 'H'; } 86 | 87 | // Region conditions 88 | bool IsUS() { return region == 'U'; } 89 | bool IsEU() { return region == 'E'; } 90 | 91 | // Game Conditions 92 | bool IsIII() { return game == '3'; } 93 | bool IsVC () { return game == 'V'; } 94 | bool IsSA () { return game == 'S'; } 95 | bool IsIV () { return game == 'I'; } 96 | bool IsEFLC(){ return game == 'E'; } 97 | 98 | // Detects game, region and version; returns false if could not detect it 99 | bool Detect(); 100 | 101 | // Gets the game version as text, the buffer must contain at least 32 bytes of space. 102 | char* GetVersionText(char* buffer) 103 | { 104 | if(this->IsUnknown()) 105 | { 106 | strcpy(buffer, "UNKNOWN GAME"); 107 | return buffer; 108 | } 109 | 110 | const char* g = this->IsIII() ? "III" : this->IsVC() ? "VC" : this->IsSA() ? "SA" : this->IsIV() ? "IV" : this->IsEFLC() ? "EFLC" : "UNK"; 111 | const char* r = this->IsUS()? "US" : this->IsEU()? "EURO" : "UNK_REGION"; 112 | const char* s = this->IsSteam()? "Steam" : ""; 113 | if (this->IsIII() || this->IsVC() || this->IsSA()) 114 | sprintf(buffer, "GTA %s %d.%d %s%s", g, major, minor, r, s); 115 | else 116 | sprintf(buffer, "GTA %s %d.%d.%d.%d %s%s", g, major, minor, majorRevision, minorRevision, r, s); 117 | return buffer; 118 | } 119 | 120 | 121 | public: 122 | // Raises a error saying that you could not detect the game version 123 | void RaiseCouldNotDetect() 124 | { 125 | MessageBoxA(0, 126 | "Could not detect the game version\nContact the mod creator!", 127 | PluginName, MB_ICONERROR 128 | ); 129 | } 130 | 131 | // Raises a error saying that the exe version is incompatible (and output the exe name) 132 | void RaiseIncompatibleVersion() 133 | { 134 | char buf[128], v[32]; 135 | sprintf(buf, 136 | "An incompatible exe version has been detected! (%s)\nContact the mod creator!", 137 | GetVersionText(v) 138 | ); 139 | MessageBoxA(0, buf, PluginName, MB_ICONERROR); 140 | } 141 | }; 142 | #else // INJECTOR_GVM_DUMMY 143 | class game_version_manager 144 | { 145 | public: 146 | bool Detect() { return true; } 147 | }; 148 | #endif // INJECTOR_GVM_DUMMY 149 | #endif // INJECTOR_OWN_GVM 150 | 151 | 152 | /* 153 | * address_manager 154 | * Address translator from 1.0 executables to other executables offsets 155 | * Inherits from game_version_manager ;) 156 | */ 157 | class address_manager : public game_version_manager 158 | { 159 | private: 160 | address_manager() 161 | { 162 | this->Detect(); 163 | } 164 | 165 | // You could implement your translator for the address your plugin uses 166 | // If not implemented, the translator won't translate anything, just return the samething as before 167 | #ifdef INJECTOR_GVM_HAS_TRANSLATOR 168 | void* translator(void* p); 169 | #else 170 | void* translator(void* p) { return p; } 171 | #endif 172 | 173 | public: 174 | // Translates address p to the running executable pointer 175 | void* translate(void* p) 176 | { 177 | return translator(p); 178 | } 179 | 180 | 181 | public: 182 | // Address manager singleton 183 | static address_manager& singleton() 184 | { 185 | static address_manager m; 186 | return m; 187 | } 188 | 189 | // Static version of translate() 190 | static void* translate_address(void* p) 191 | { 192 | return singleton().translate(p); 193 | } 194 | 195 | // 196 | static void set_name(const char* modname) 197 | { 198 | singleton().PluginName = modname; 199 | } 200 | 201 | public: 202 | // Functors for memory translation: 203 | 204 | // Translates aslr translator 205 | struct fn_mem_translator_aslr 206 | { 207 | void* operator()(void* p) const 208 | { 209 | static uintptr_t module = (uintptr_t)GetModuleHandle(NULL); 210 | return (void*)((uintptr_t)(p)-(0x400000 - module)); 211 | } 212 | }; 213 | 214 | // Translates nothing translator 215 | struct fn_mem_translator_nop 216 | { 217 | void* operator()(void* p) const 218 | { return p; } 219 | }; 220 | 221 | // Real translator 222 | struct fn_mem_translator 223 | { 224 | void* operator()(void* p) const 225 | { return translate_address(p); } 226 | }; 227 | }; 228 | 229 | #endif // #if 1 230 | 231 | 232 | } -------------------------------------------------------------------------------- /Shaders/particles.fx: -------------------------------------------------------------------------------- 1 | // 2 | // Standard Effect 3 | // 4 | #include "global.h" 5 | 6 | shared float4x4 cmWorldView : cmWorldView; //WORLDVIEW 7 | shared float4x4 cmWorldMat : cmWorldMat; //local to world matrix 8 | shared float3 cvLocalEyePos : cvLocalEyePos; //LOCALEYEPOS; 9 | shared float4 cvTextureOffset : cvTextureOffset; //TEXTUREANIMOFFSET; 10 | 11 | 12 | shared float cfBrightness : cfBrightness; //STANDARD_BRIGHTNESS 13 | shared float4 cvLocalCenter : cvLocalCenter; //LOCALCENTER; 14 | shared float4 cvBaseAlphaRef : cvBaseAlphaRef; //BASEALPHAREF; 15 | shared float4 cvBlurParams : cvBlurParams; //IS_MOTIONBLUR_VIGNETTED; 16 | shared float4 cmPrevWorldViewProj : cmPrevWorldViewProj; //BASEALPHAREF; 17 | //shared float4 cfSplitScreenUVScale : cfSplitScreenUVScale; // PC edit - no splitscreen yet 18 | shared float cfMipMapBias : cfMipMapBias; 19 | shared float4 cvFogColour : cvFogColour; 20 | 21 | 22 | static const float FuzzWidth = 0.15f;//0.05f;//changed from 0.05 to 0.15 by Lee Rosenbaum Aug 23, 2006; 23 | 24 | static const float MaxParticleSize = 0.75f; 25 | static const float MaterialShininess = 10; 26 | 27 | // Pro-Street these should not change much at all (its the sun) 28 | //float4 cvLightColour; 29 | float4 cavLightColours[10] : cavLightColours;// PC / Carbon edit - it uses arrays 30 | //float4 cvLightPosition; 31 | float4 cavLightPositions[10] : cavLightPositions; 32 | 33 | // these should be artist controlled 34 | float3 ambient_colour = float3(0.72,0.8,0.9); // PC edit - PC version has it in a variable, not a static definition... 35 | //#define ambient_colour ( float3(0.72,0.8,0.9) ) 36 | 37 | float magnitude_debug = 1.0; 38 | 39 | DECLARE_TEXTURE(MISCMAP1_TEXTURE) 40 | sampler MISCMAP1_SAMPLER = sampler_state // backbuffer for screen distortion 41 | { 42 | ASSIGN_TEXTURE(MISCMAP1_TEXTURE) 43 | AddressU = CLAMP; 44 | AddressV = CLAMP; 45 | DECLARE_MIPFILTER(LINEAR) 46 | DECLARE_MINFILTER(LINEAR) 47 | DECLARE_MAGFILTER(LINEAR) 48 | }; 49 | 50 | DECLARE_TEXTURE(OPACITYMAP_TEXTURE) 51 | sampler OPACITY_SAMPLER = sampler_state // rain alpha texture 52 | { 53 | ASSIGN_TEXTURE(OPACITYMAP_TEXTURE) 54 | AddressU = CLAMP; 55 | AddressV = CLAMP; 56 | DECLARE_MIPFILTER(LINEAR) 57 | DECLARE_MINFILTER(LINEAR) 58 | DECLARE_MAGFILTER(LINEAR) 59 | }; 60 | DECLARE_TEXTURE(DIFFUSEMAP_TEXTURE) // PC edit - these NEED to be here for the shader to work! 61 | sampler DIFFUSE_SAMPLER = sampler_state 62 | { 63 | ASSIGN_TEXTURE(DIFFUSEMAP_TEXTURE) 64 | AddressU = WRAP; 65 | AddressV = WRAP; 66 | DECLARE_MIPFILTER(LINEAR) 67 | DECLARE_MINFILTER(LINEAR) 68 | DECLARE_MAGFILTER(LINEAR) 69 | }; 70 | 71 | DECLARE_TEXTURE(NORMALMAP_TEXTURE) 72 | sampler NORMALMAP_SAMPLER = sampler_state 73 | { 74 | ASSIGN_TEXTURE(NORMALMAP_TEXTURE) 75 | AddressU = WRAP; 76 | AddressV = WRAP; 77 | DECLARE_MIPFILTER(LINEAR) 78 | DECLARE_MINFILTER(LINEAR) 79 | DECLARE_MAGFILTER(LINEAR) 80 | }; 81 | 82 | DECLARE_TEXTURE(HEIGHTMAP_TEXTURE) 83 | sampler HEIGHTMAP_SAMPLER = sampler_state 84 | { 85 | ASSIGN_TEXTURE(HEIGHTMAP_TEXTURE) 86 | }; 87 | 88 | struct VS_INPUT 89 | { 90 | float4 position : POSITION; 91 | float4 color : COLOR; 92 | float4 tex : TEXCOORD; 93 | float4 size : TEXCOORD1; 94 | //int4 light_index : BLENDINDICES; 95 | int4 light_index : TEXCOORD2; 96 | }; 97 | 98 | struct VtoP_NormalMapped 99 | { 100 | float4 position : POSITION; 101 | float4 color : COLOR0; 102 | float3 lightColor : COLOR1; 103 | float4 tex : TEXCOORD0; 104 | float4 tex1 : TEXCOORD1; 105 | // vector from the vertex to the light, in tangent space 106 | float3 to_light_tan : TEXCOORD2; 107 | // Half-angle vector, needed for per-pixel specular, in tangent space 108 | //float3 half_angle_tan : TEXCOORD3; 109 | float4 position2 : TEXCOORD3; 110 | }; 111 | 112 | struct VtoP 113 | { 114 | float4 position : POSITION; 115 | float4 color : COLOR0; 116 | float4 tex : TEXCOORD0; 117 | float4 tex1 : TEXCOORD1; 118 | }; 119 | 120 | struct PS_OUTPUT 121 | { 122 | float4 color : COLOR0; 123 | }; 124 | 125 | struct VtoP_Depth 126 | { 127 | float4 position : POSITION; 128 | float dist : COLOR0; 129 | }; 130 | 131 | //----------------------------------------------------------------------------- 132 | // PARTICLES 133 | // 134 | 135 | float3x3 BuildRotate(float angle, float3 rotAxis) 136 | { 137 | float3x3 m; 138 | // float fSin = sin(angle); 139 | // float fCos = cos(angle); 140 | float2 sc; 141 | sincos(angle,sc.x,sc.y); 142 | float3 axis = normalize(rotAxis); 143 | 144 | float3 cosAxis = (1.0f - sc.y) * axis; 145 | float3 sinAxis = sc.x * axis; 146 | m[0] = cosAxis.x * axis; 147 | m[1] = cosAxis.y * axis; 148 | m[2] = cosAxis.z * axis; 149 | m[0][0] += sc.y; 150 | m[0][1] += sinAxis.z; 151 | m[0][2] -= sinAxis.y; 152 | m[1][0] -= sinAxis.z; 153 | m[1][1] += sc.y; 154 | m[1][2] += sinAxis.x; 155 | m[2][0] += sinAxis.y; 156 | m[2][1] -= sinAxis.x; 157 | m[2][2] += sc.y; 158 | 159 | return m; 160 | } 161 | 162 | VtoP_NormalMapped vertex_shader_particles(const VS_INPUT IN) 163 | { 164 | VtoP_NormalMapped OUT; 165 | // Offset the vertex by the particle size 166 | float3 right = cmWorldView._m00_m10_m20; 167 | float3 up = cmWorldView._m01_m11_m21; 168 | float3 facing = cmWorldView._m02_m12_m22; 169 | 170 | // Rotate the up and right around the facing 171 | float angle = IN.size.z; 172 | if( angle > 0 ) 173 | { 174 | float3x3 rotation = BuildRotate(angle, facing); 175 | right = mul(right, rotation); 176 | up = mul(up, rotation); 177 | } 178 | 179 | // Add offset from particle midpoint to the outside vertices 180 | float4 pv = IN.position; 181 | float3 offset = right * IN.size.x + up * IN.size.y; 182 | pv.xyz += offset; 183 | 184 | // Cap the screen size of any particle 185 | float4 worldCornerPos = pv; 186 | pv = world_position(pv); 187 | 188 | float3 pvn = pv.xyz/pv.w; 189 | float4 pc = world_position(IN.position); 190 | float3 pcn = pc.xyz/pc.w; 191 | float size = distance(pvn.xy,pcn.xy); 192 | float new_size = min(size, MaxParticleSize); 193 | float scale = new_size/size; 194 | pv = lerp(pc,pv,scale); 195 | 196 | // Each particle is affected by one light in the light array 197 | // Read which one it is: 198 | int lightIndex = IN.light_index.x; 199 | //float3 lightPos = cvLightPosition.xyz; 200 | float3 lightPos = cavLightPositions[lightIndex].xyz; // PC edit - it uses arrays 201 | 202 | float3 worldPos = mul(IN.position, cmWorldMat); 203 | float3 toLightSource = normalize(lightPos - worldPos); 204 | 205 | // Create the matrix which transforms from world space to tangent space 206 | float3 tangent = right; 207 | float3 binormal = up; 208 | float3 normal = -facing; 209 | float3x3 matTSpace = transpose(float3x3( tangent, binormal, normal )); 210 | 211 | OUT.to_light_tan = mul(toLightSource, matTSpace); 212 | 213 | float3 toEyeWorld = normalize(cvLocalEyePos - worldPos); 214 | float3 toEyeTan = mul(toEyeWorld, matTSpace); 215 | 216 | // Calculate the half-angle vector for per-pixel specular (maybe this is overkill for particles) 217 | //OUT.half_angle_tan = (toEyeTan + OUT.to_light_tan) * 0.5; 218 | 219 | float3 lightColour = cavLightColours[lightIndex].xyz; // PC edit - it uses arrays 220 | //OUT.lightColor = 1;//lightColour; 221 | OUT.lightColor = lightColour; 222 | 223 | OUT.position = OUT.position2 = pv; 224 | OUT.color = saturate(IN.color * 2); 225 | //OUT.color.xyz *= cvAmbientColour.xyz; 226 | 227 | OUT.tex = IN.tex + cvTextureOffset; 228 | 229 | // Convert from screen space (-1 to 1) to texture coordinate space (0.0 to 1.0) 230 | float distance = pv.z / pv.w; 231 | OUT.tex1.x = (0.5 * pv.x / pv.w) + 0.5; 232 | OUT.tex1.y = (-0.5 * pv.y / pv.w) + 0.5; 233 | //OUT.tex1.y *= cfSplitScreenUVScale[1]; // Split screen adjustment // PC edit - no splitscreen yet 234 | //OUT.tex1.y += cfSplitScreenUVScale[0]; // Split screen adjustment 235 | 236 | // Distance to the pixel in the depth buffer 237 | OUT.tex1.z = distance; 238 | 239 | // We cannot use FuzzWidth directly because we are performing these operations in the depth buffer. 240 | // FuzzWidth is the amount of play that the sprites have with opaque objects. If a sprite is within FuzzWidth of an Opaque object 241 | // the sprite will get blurred, fading it in nicely with the opaque object and smoothing the harsh edges out 242 | 243 | // Since Ztesting is off, it also controls whether or not we see the sprite if an opaque object is behind it 244 | 245 | // We need to scale FuzzWidth down if the sprite is near the back of the depth buffer. For instance, FuzzWidth could correspond to 1 cm 246 | // at the start of the depth buffer, but it could be 100 meters at the end of it 247 | 248 | // Calculate the distance from the particle to the camera in world coordinates 249 | OUT.tex1.w = length(cvLocalEyePos - IN.position) ; 250 | 251 | 252 | return OUT; 253 | } 254 | 255 | PS_OUTPUT pixel_shader_particles(const VtoP_NormalMapped IN) 256 | { 257 | PS_OUTPUT OUT; 258 | float shadow = 1;//DoShadow( IN.shadowTex, 1 ) * 0.5 + 0.5; 259 | 260 | float4 baseColour = tex2D(DIFFUSE_SAMPLER, IN.tex) * IN.color; 261 | 262 | float depth = tex2D(HEIGHTMAP_SAMPLER, IN.tex1.xy).x; 263 | 264 | 265 | float zFar = 10000; 266 | float zNear = 0.5; 267 | float Q = zFar / (zFar - zNear); 268 | float zDist = (-Q * zNear / (depth - Q)); 269 | 270 | float depthBufferDistToParticle = IN.position2.z / IN.position2.w; 271 | float distanceToParticle = (-Q * zNear / (depthBufferDistToParticle - Q)); 272 | 273 | float distanceBetweenParticleAndGround = abs(zDist - distanceToParticle); 274 | float fuzzz = saturate(distanceBetweenParticleAndGround * 1); 275 | 276 | //fuzzz = 1; 277 | 278 | // calculate the normal map 279 | float3 normal = tex2D(NORMALMAP_SAMPLER, IN.tex) * 2 - 1; 280 | 281 | normal = normal * magnitude_debug ; 282 | 283 | // Apply diffuse lighting 284 | float3 toLight = normalize(IN.to_light_tan); 285 | float nDotL = saturate(dot(normal, toLight)); 286 | 287 | float3 diffuseColour = nDotL * IN.lightColor; 288 | //diffuseColour.xyz = 1; 289 | 290 | // specular calculations 291 | //float3 half_angle = normalize(IN.half_angle_tan); 292 | //float nDotH = saturate(dot(normal, half_angle)); 293 | 294 | //float3 MaterialSpecular = float3(0.4, 0.4, 0.4); 295 | 296 | //float3 specular = MaterialSpecular * pow(nDotH, MaterialShininess); 297 | 298 | OUT.color.rgb = fuzzz * shadow * baseColour * (ambient_colour + diffuseColour);// + specular); 299 | OUT.color.a = baseColour.a * shadow * fuzzz; 300 | 301 | 302 | //OUT.color.rgb = CompressColourSpace(OUT.color.rgb); // PC edit - we don't really need this here 303 | 304 | return OUT; 305 | } 306 | 307 | technique fuzzz 308 | { 309 | pass p0 310 | { 311 | VertexShader = compile vs_1_1 vertex_shader_particles(); 312 | PixelShader = compile ps_2_0 pixel_shader_particles(); 313 | } 314 | } 315 | 316 | technique no_fuzzz 317 | { 318 | pass p0 319 | { 320 | VertexShader = compile vs_1_1 vertex_shader_particles(); 321 | PixelShader = compile ps_2_0 pixel_shader_particles(); 322 | } 323 | } 324 | 325 | 326 | /////////////////////////////////////////////////////////////////////////////////////// 327 | // 328 | // RAINDROPS - CUSTOM SHADERS FOR RAINDROPS - this is needed because raindrops are basically invisible otherwise... 329 | // 330 | /////////////////////////////////////////////////////////////////////////////////////// 331 | 332 | PS_OUTPUT pixel_shader_raindrop(const VtoP_NormalMapped IN) 333 | { 334 | PS_OUTPUT OUT; 335 | float shadow = 1;//DoShadow( IN.shadowTex, 1 ) * 0.5 + 0.5; 336 | 337 | float4 baseColour = tex2D(DIFFUSE_SAMPLER, IN.tex) * IN.color * 2; 338 | 339 | float depth = tex2D(HEIGHTMAP_SAMPLER, IN.tex1.xy).x; 340 | 341 | 342 | float zFar = 10000; 343 | float zNear = 0.5; 344 | float Q = zFar / (zFar - zNear); 345 | float zDist = (-Q * zNear / (depth - Q)); 346 | 347 | float depthBufferDistToParticle = IN.position2.z / IN.position2.w; 348 | float distanceToParticle = (-Q * zNear / (depthBufferDistToParticle - Q)); 349 | 350 | float distanceBetweenParticleAndGround = abs(zDist - distanceToParticle); 351 | float fuzzz = saturate(distanceBetweenParticleAndGround * 1); 352 | 353 | //fuzzz = 1; 354 | 355 | // calculate the normal map 356 | float3 normal = tex2D(NORMALMAP_SAMPLER, IN.tex) * 2 - 1; 357 | 358 | normal = normal * magnitude_debug ; 359 | 360 | // Apply diffuse lighting 361 | float3 toLight = normalize(IN.to_light_tan); 362 | float nDotL = saturate(dot(normal, toLight)); 363 | 364 | float3 diffuseColour = nDotL * IN.lightColor; 365 | //diffuseColour.xyz = 1; 366 | 367 | // specular calculations 368 | //float3 half_angle = normalize(IN.half_angle_tan); 369 | //float nDotH = saturate(dot(normal, half_angle)); 370 | 371 | //float3 MaterialSpecular = float3(0.4, 0.4, 0.4); 372 | 373 | //float3 specular = MaterialSpecular * pow(nDotH, MaterialShininess); 374 | 375 | OUT.color.rgb = fuzzz * shadow * baseColour * (ambient_colour + diffuseColour);// + specular); 376 | OUT.color.a = baseColour.a * shadow * fuzzz; 377 | 378 | return OUT; 379 | } 380 | 381 | technique raindrop 382 | { 383 | pass p0 384 | { 385 | FogEnable = FALSE; // NECESSARY - it's affected by sky fog otherwise! 386 | 387 | VertexShader = compile vs_1_1 vertex_shader_particles(); 388 | PixelShader = compile ps_2_0 pixel_shader_raindrop(); 389 | } 390 | } 391 | 392 | /////////////////////////////////////////////////////////////////////////////////////// 393 | // 394 | // FLARES 395 | // 396 | /////////////////////////////////////////////////////////////////////////////////////// 397 | 398 | 399 | struct VS_INPUT_FLARES 400 | { 401 | float4 position : POSITION; 402 | float4 color : COLOR; 403 | float4 tex : TEXCOORD; 404 | float4 size : TEXCOORD1; 405 | }; 406 | 407 | struct VtoP_FLARES 408 | { 409 | float4 position : POSITION; 410 | float4 color : COLOR0; 411 | float4 tex : TEXCOORD0; 412 | float4 tex1 : TEXCOORD1; 413 | }; 414 | 415 | VtoP_FLARES vertex_shader_flares(const VS_INPUT_FLARES IN) 416 | { 417 | VtoP_FLARES OUT; 418 | // Offset the vertex by the particle size 419 | float3 right = cmWorldView._m00_m10_m20; 420 | float3 up = cmWorldView._m01_m11_m21; 421 | float3 facing = cmWorldView._m02_m12_m22; 422 | float isTrailFlare = IN.size.w; 423 | 424 | float angle = IN.size.z; 425 | // Rotate the up and right around the facing 426 | if( angle > 0 ) 427 | { 428 | float3x3 rotation = BuildRotate(angle, facing); 429 | right = mul(right, rotation); 430 | up = mul(up, rotation); 431 | } 432 | 433 | float4 pv = IN.position; 434 | pv.xyz += right * IN.size.x + up * IN.size.y; 435 | 436 | // Cap the screen size of any particle 437 | pv = world_position(pv); 438 | 439 | OUT.position = pv; 440 | OUT.color = IN.color; 441 | OUT.tex = IN.tex; 442 | OUT.tex.w = cfMipMapBias; // bias the mipmapping 443 | // OUT.tex1 = IN.size; 444 | 445 | 446 | // Convert from screen space (-1 to 1) to texture coordinate space (0.0 to 1.0) 447 | float distance = pv.z / pv.w; 448 | OUT.tex1.x = (0.5 * pv.x / pv.w) + 0.5; 449 | OUT.tex1.y = (-0.5 * pv.y / pv.w) + 0.5; 450 | OUT.tex1.z = distance; // Distance to the pixel in the depth buffer 451 | //OUT.tex1.y *= cfSplitScreenUVScale[1]; // Split screen adjustment 452 | //OUT.tex1.y += cfSplitScreenUVScale[0]; // Split screen adjustment 453 | 454 | // We cannot use FuzzWidth directly because we are performing these operations in the depth buffer. 455 | // FuzzWidth is the amount of play that the sprites have with opaque objects. If a sprite is within FuzzWidth of an Opaque object 456 | // the sprite will get blurred, fading it in nicely with the opaque object and smoothing the harsh edges out 457 | 458 | // Since Ztesting is off, it also controls whether or not we see the sprite if an opaque object is behind it 459 | 460 | // We need to scale FuzzWidth down if the sprite is near the back of the depth buffer. For instance, FuzzWidth could correspond to 1 cm 461 | // at the start of the depth buffer, but it could be 100 meters at the end of it 462 | OUT.tex1.w = saturate(FuzzWidth + distance * (-FuzzWidth)); 463 | 464 | 465 | return OUT; 466 | } 467 | 468 | 469 | 470 | float4 pixel_shader_flares(const VtoP_FLARES IN) : COLOR 471 | { 472 | float4 diffuse = tex2Dbias(DIFFUSE_SAMPLER, IN.tex); 473 | 474 | float scaled_fuzz_width = IN.tex1.w; 475 | float depth = tex2D(HEIGHTMAP_SAMPLER,IN.tex1.xy).x; 476 | float fuzzz = saturate((scaled_fuzz_width*0.4 - (IN.tex1.z - depth)) / (scaled_fuzz_width)); 477 | 478 | 479 | float4 result; 480 | result = diffuse; 481 | result *= IN.color;//*cvBaseAlphaRef.y; 482 | // Apply a tone mapping to fake a HDR 483 | result.xyz = result.xyz / (1.5-result.xyz); 484 | //result.xyz = pow(result.xyz, 1.5)*1.5; 485 | //result.xyz = result.x > 0.95 ? 1.0f : 0.0f; 486 | // cvBaseAlphaRef.x is set to 0 in players views so the fuzzz used and 1 to disable the fuzz 487 | result.w *= saturate(cvBaseAlphaRef.x + fuzzz); 488 | 489 | //result.xyz = fuzzz;//(scaled_fuzz_width - (IN.tex1.z - depth)) / FuzzWidth; 490 | //result.w = 1; 491 | //result.xyz = CompressColourSpace(result.xyz); 492 | 493 | return result; 494 | } 495 | 496 | technique flares 497 | { 498 | pass p0 499 | { 500 | VertexShader = compile vs_1_1 vertex_shader_flares(); 501 | PixelShader = compile ps_2_0 pixel_shader_flares(); 502 | } 503 | } 504 | 505 | /////////////////////////////////////////////////////////////////////////////////////// 506 | // 507 | // STREAK FLARES 508 | // 509 | /////////////////////////////////////////////////////////////////////////////////////// 510 | 511 | struct VtoP_SFLARES 512 | { 513 | float4 position : POSITION; 514 | float4 color : COLOR0; 515 | float4 tex : TEXCOORD0; 516 | }; 517 | 518 | VtoP_SFLARES vertex_shader_streak_flares(const VS_INPUT_FLARES IN) 519 | { 520 | VtoP_SFLARES OUT; 521 | // Offset the vertex by the particle size 522 | float3 right = cmWorldView._m00_m10_m20; 523 | float3 up = cmWorldView._m01_m11_m21; 524 | float3 facing = cmWorldView._m02_m12_m22; 525 | float doStreakFlare = IN.size.w; 526 | float4 pv = IN.position; 527 | float4 vertexColour = IN.color; 528 | float4 tex = IN.tex; 529 | 530 | if( doStreakFlare > 0 ) 531 | { 532 | //float cameraSpeed = cvBaseAlphaRef.y * 75.0; 533 | float cameraSpeed = cvBaseAlphaRef.y; 534 | float nosAmount = cvBaseAlphaRef.w; 535 | 536 | // Add some speed for NOS 537 | // 538 | cameraSpeed += nosAmount*20; 539 | 540 | // 541 | // Flare is stretch to create a flare trail for Sense of Speed 542 | // 543 | float4 currFramePos = mul(IN.position, cmWorldViewProj); 544 | 545 | // Redice length if camera veleocity is off centre in the x direction 546 | cameraSpeed = lerp(cameraSpeed, 0, saturate(abs(cvBaseAlphaRef.x)/0.05)); 547 | //cameraSpeed = lerp(cameraSpeed, 0, saturate(abs(barx)/0.05)); 548 | 549 | float4 prevFramePos = currFramePos; 550 | prevFramePos.zw += 3 + saturate(cameraSpeed / 140) * 45; // Stretch flare back 551 | currFramePos += normalize(currFramePos - prevFramePos) * 10; // Stretch flare forward 552 | 553 | 554 | 555 | // Build the poly 556 | float3 prevFrameDir = prevFramePos.xyz/prevFramePos.w - currFramePos.xyz/currFramePos.w; 557 | float offsetWidth = min(abs(IN.size.x), abs(IN.size.y)); 558 | offsetWidth *= 0.2; 559 | float4 currFrameOffset = mul(IN.position + float4(offsetWidth, offsetWidth, offsetWidth, 0), cmWorldViewProj); 560 | float currFrameSize = distance(currFrameOffset.xy, currFramePos.xy); 561 | float3 prevFrameDirTanget = normalize(prevFrameDir); 562 | // Rotate by 90 degrees to get tangent 563 | prevFrameDirTanget.xy = prevFrameDirTanget.yx; 564 | prevFrameDirTanget.x = -prevFrameDirTanget.x; 565 | prevFrameDirTanget *= currFrameSize; 566 | 567 | // Push the tex coord futher along the streak for longer streaks 568 | float texUlength = saturate(length(prevFrameDir.xyz)/0.1); // 0.3 569 | tex.x = IN.size.x > 0 ? 1/256 : texUlength; 570 | tex.y = IN.size.y > 0 ? cvBaseAlphaRef.z : cvBaseAlphaRef.z + 15.0f / 128.0f; // The v texcoord is calc in on the CPU 571 | tex.z = IN.size.x > 0 ? 0 : 1; 572 | 573 | // Cap the screen size of any particle 574 | pv = world_position(pv); // BUG - IF THIS ISNT HERE ITS NOT IN THE CAMERA 575 | 576 | pv = IN.size.x > 0 ? currFramePos : prevFramePos; 577 | // Adjust brightness for lower/higher speeds 578 | vertexColour *= saturate(cameraSpeed / 95); 579 | 580 | // Add the offset to create width for the streak along the tangent 581 | // 582 | pv.xy += sign(IN.size.y) * prevFrameDirTanget; 583 | //pv.z = 1; 584 | } 585 | else 586 | { 587 | // Push offscreen to clip 588 | pv = 1000; 589 | } 590 | 591 | OUT.position = pv; 592 | OUT.color = vertexColour; // BUG - ITS TRANSPARENT ON PC -- cameraSpeed is INCORRECT 593 | OUT.tex = tex; 594 | OUT.tex.w = -5; // bias the mipmapping 595 | //OUT.color = IN.color; // uncomment this to make them render always 596 | 597 | return OUT; 598 | } 599 | 600 | 601 | float4 pixel_shader_streak_flares(const VtoP_SFLARES IN) : COLOR 602 | { 603 | float4 result = tex2Dbias(DIFFUSE_SAMPLER, IN.tex);; 604 | result *= IN.color; 605 | 606 | return result; 607 | } 608 | 609 | technique streak_flares 610 | { 611 | pass p0 612 | { 613 | VertexShader = compile vs_1_1 vertex_shader_streak_flares(); 614 | PixelShader = compile ps_2_0 pixel_shader_streak_flares(); 615 | } 616 | } 617 | 618 | 619 | /////////////////////////////////////////////////////////////////////////////////////// 620 | // 621 | // Onscreen rain particle effect 622 | // 623 | struct VtoP_RAIN 624 | { 625 | float4 position : POSITION; 626 | float4 tex : TEXCOORD0; 627 | }; 628 | 629 | VtoP_RAIN vertex_shader_passthru(const VS_INPUT IN) 630 | { 631 | VtoP_RAIN OUT; 632 | OUT.position = screen_position(IN.position); 633 | OUT.position.w = 1.0f; 634 | OUT.tex = IN.tex; 635 | 636 | return OUT; 637 | } 638 | 639 | float4 pixel_shader_onscreen_distort(const VtoP_RAIN IN) : COLOR0 640 | { 641 | float4 distortion = tex2D(DIFFUSE_SAMPLER, IN.tex); 642 | float2 offset = distortion.gb * cvLocalCenter.ba + cvLocalCenter.rg; 643 | float4 background = tex2D(MISCMAP1_SAMPLER, offset); 644 | 645 | // The opacity map has four different raindrop texture tiled horizontally. The 646 | // offset into this texure is stored in cvBaseAlphaRef.y 647 | // 648 | offset = IN.tex; 649 | offset.x = cvBaseAlphaRef.y + IN.tex.x*0.25; 650 | float4 opacity = tex2D(OPACITY_SAMPLER, offset); 651 | 652 | float4 result; 653 | result = background * opacity.y; 654 | result.w = opacity.r * cvBaseAlphaRef.x; 655 | 656 | //result = opacity; 657 | 658 | return result; 659 | } 660 | 661 | technique onscreen_distort 662 | { 663 | pass p0 664 | { 665 | VertexShader = compile vs_1_1 vertex_shader_passthru(); 666 | PixelShader = compile ps_2_0 pixel_shader_onscreen_distort(); 667 | } 668 | } 669 | 670 | //#include "ZPrePass_fx.h" // PC edit - no need for ZPrePass - PC renderer doesn't use it 671 | 672 | -------------------------------------------------------------------------------- /includes/injector/injector.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Injectors - Base Header 3 | * 4 | * Copyright (C) 2012-2014 LINK/2012 5 | * Copyright (C) 2014 Deji 6 | * 7 | * This software is provided 'as-is', without any express or implied 8 | * warranty. In no event will the authors be held liable for any damages 9 | * arising from the use of this software. 10 | * 11 | * Permission is granted to anyone to use this software for any purpose, 12 | * including commercial applications, and to alter it and redistribute it 13 | * freely, subject to the following restrictions: 14 | * 15 | * 1. The origin of this software must not be misrepresented; you must not 16 | * claim that you wrote the original software. If you use this software 17 | * in a product, an acknowledgment in the product documentation would be 18 | * appreciated but is not required. 19 | * 20 | * 2. Altered source versions must be plainly marked as such, and must not be 21 | * misrepresented as being the original software. 22 | * 23 | * 3. This notice may not be removed or altered from any source 24 | * distribution. 25 | * 26 | */ 27 | #pragma once 28 | #define INJECTOR_HAS_INJECTOR_HPP 29 | #include 30 | #include 31 | #include 32 | #include "gvm/gvm.hpp" 33 | /* 34 | The following macros (#define) are relevant on this header: 35 | 36 | INJECTOR_GVM_HAS_TRANSLATOR 37 | If defined, the user should provide their own address_manager::translator function. 38 | That function is responssible for translating a void pointer (that mayn't be an actual pointer) into an actual address. 39 | The meaning of that void pointer will be made by YOU when you send it to the functions that receive pointers on this library. 40 | The default translator does nothing but returns that void pointer as the address. 41 | 42 | INJECTOR_GVM_OWN_DETECT 43 | If defined, the user should provide it's own game detection function thought game_version_manager::Detect 44 | By default it provide an good detection for the Grand Theft Auto series. 45 | 46 | INJECTOR_GVM_PLUGIN_NAME 47 | If this is defined, it will be used as the plugin name used at error messages. 48 | By default it will use ""Unknown Plugin Name" 49 | 50 | INJECTOR_GVM_DUMMY 51 | If defined, the game_version_manager will be a dummy object 52 | By default it provides a nice gvm for Grand Theft Auto series 53 | 54 | INJECTOR_OWN_GVM 55 | If defined, the game_version_manager should be implemented by the user before including this library. 56 | By default it provides a nice gvm for Grand Theft Auto series 57 | */ 58 | #include "gvm/gvm.hpp" 59 | 60 | 61 | 62 | namespace injector 63 | { 64 | 65 | 66 | /* 67 | * auto_pointer 68 | * Casts itself to another pointer type in the lhs 69 | */ 70 | union auto_pointer 71 | { 72 | protected: 73 | friend union memory_pointer_tr; 74 | template friend union basic_memory_pointer; 75 | 76 | void* p; 77 | uintptr_t a; 78 | 79 | public: 80 | auto_pointer() : p(0) {} 81 | auto_pointer(const auto_pointer& x) : p(x.p) {} 82 | explicit auto_pointer(void* x) : p(x) {} 83 | explicit auto_pointer(uint32_t x) : a(x) {} 84 | 85 | bool is_null() const { return this->p != nullptr; } 86 | 87 | #if __cplusplus >= 201103L || _MSC_VER >= 1800 88 | explicit operator bool() const { return is_null(); } 89 | #endif 90 | 91 | auto_pointer get() const { return *this; } 92 | template T* get() const { return (T*) this->p; } 93 | template T* get_raw() const { return (T*) this->p; } 94 | 95 | template 96 | operator T*() const { return reinterpret_cast(p); } 97 | }; 98 | 99 | /* 100 | * basic_memory_pointer 101 | * A memory pointer class that is capable of many operations, including address translation 102 | * MemTranslator is the translator functor 103 | */ 104 | template 105 | union basic_memory_pointer 106 | { 107 | protected: 108 | void* p; 109 | uintptr_t a; 110 | 111 | // Translates address p to the running executable pointer 112 | static auto_pointer memory_translate(void* p) 113 | { 114 | return auto_pointer(MemTranslator()(p)); 115 | } 116 | 117 | public: 118 | basic_memory_pointer() : p(nullptr) {} 119 | basic_memory_pointer(std::nullptr_t) : p(nullptr) {} 120 | basic_memory_pointer(uintptr_t x) : a(x) {} 121 | basic_memory_pointer(const auto_pointer& x) : p(x.p) {} 122 | basic_memory_pointer(const basic_memory_pointer& rhs) : p(rhs.p) {} 123 | 124 | template 125 | basic_memory_pointer(T* x) : p((void*)x) {} 126 | 127 | 128 | 129 | 130 | // Gets the translated pointer (plus automatic casting to lhs) 131 | auto_pointer get() const { return memory_translate(p); } 132 | 133 | // Gets the translated pointer (casted to T*) 134 | template T* get() const { return get(); } 135 | 136 | // Gets the raw pointer, without translation (casted to T*) 137 | template T* get_raw() const { return auto_pointer(p); } 138 | 139 | // This type can get assigned from void* and uintptr_t 140 | basic_memory_pointer& operator=(void* x) { return p = x, *this; } 141 | basic_memory_pointer& operator=(uintptr_t x) { return a = x, *this; } 142 | 143 | /* Arithmetic */ 144 | basic_memory_pointer operator+(const basic_memory_pointer& rhs) const 145 | { return basic_memory_pointer(this->a + rhs.a); } 146 | 147 | basic_memory_pointer operator-(const basic_memory_pointer& rhs) const 148 | { return basic_memory_pointer(this->a - rhs.a); } 149 | 150 | basic_memory_pointer operator*(const basic_memory_pointer& rhs) const 151 | { return basic_memory_pointer(this->a * rhs.a); } 152 | 153 | basic_memory_pointer operator/(const basic_memory_pointer& rhs) const 154 | { return basic_memory_pointer(this->a / rhs.a); } 155 | 156 | 157 | /* Comparision */ 158 | bool operator==(const basic_memory_pointer& rhs) const 159 | { return this->a == rhs.a; } 160 | 161 | bool operator!=(const basic_memory_pointer& rhs) const 162 | { return this->a != rhs.a; } 163 | 164 | bool operator<(const basic_memory_pointer& rhs) const 165 | { return this->a < rhs.a; } 166 | 167 | bool operator<=(const basic_memory_pointer& rhs) const 168 | { return this->a <= rhs.a; } 169 | 170 | bool operator>(const basic_memory_pointer& rhs) const 171 | { return this->a > rhs.a; } 172 | 173 | bool operator>=(const basic_memory_pointer& rhs) const 174 | { return this->a >=rhs.a; } 175 | 176 | bool is_null() const { return this->p == nullptr; } 177 | uintptr_t as_int() const { return this->a; } // does not perform translation 178 | 179 | 180 | 181 | #if __cplusplus >= 201103L || _MSC_VER >= 1800 // MSVC 2013 182 | /* Conversion to other types */ 183 | explicit operator uintptr_t() const 184 | { return this->a; } // does not perform translation 185 | explicit operator bool() const 186 | { return this->p != nullptr; } 187 | #else 188 | //operator bool() -------------- Causes casting problems because of implicitness, use !is_null() 189 | //{ return this->p != nullptr; } 190 | #endif 191 | 192 | }; 193 | 194 | // Typedefs including memory translator for the above type 195 | typedef basic_memory_pointer memory_pointer; 196 | typedef basic_memory_pointer memory_pointer_raw; 197 | typedef basic_memory_pointer memory_pointer_aslr; 198 | 199 | 200 | 201 | /* 202 | * memory_pointer_tr 203 | * Stores a basic_memory_pointer as a raw pointer from translated pointer 204 | */ 205 | union memory_pointer_tr 206 | { 207 | protected: 208 | void* p; 209 | uintptr_t a; 210 | 211 | public: 212 | template 213 | memory_pointer_tr(const basic_memory_pointer& ptr) 214 | : p(ptr.get()) 215 | {} // Constructs from a basic_memory_pointer 216 | 217 | memory_pointer_tr(const auto_pointer& ptr) 218 | : p(ptr.p) 219 | {} // Constructs from a auto_pointer, probably comming from basic_memory_pointer::get 220 | 221 | memory_pointer_tr(const memory_pointer_tr& rhs) 222 | : p(rhs.p) 223 | {} // Constructs from my own type, copy constructor 224 | 225 | memory_pointer_tr(uintptr_t x) 226 | : p(memory_pointer(x).get()) 227 | {} // Constructs from a integer, translating the address 228 | 229 | memory_pointer_tr(void* x) 230 | : p(memory_pointer(x).get()) 231 | {} // Constructs from a void pointer, translating the address 232 | 233 | // Just to be method-compatible with basic_memory_pointer ... 234 | auto_pointer get() { return auto_pointer(p); } 235 | template T* get() { return get(); } 236 | template T* get_raw() { return get(); } 237 | 238 | memory_pointer_tr operator+(const uintptr_t& rhs) const 239 | { return memory_pointer_raw(this->a + rhs); } 240 | 241 | memory_pointer_tr operator-(const uintptr_t& rhs) const 242 | { return memory_pointer_raw(this->a - rhs); } 243 | 244 | memory_pointer_tr operator*(const uintptr_t& rhs) const 245 | { return memory_pointer_raw(this->a * rhs); } 246 | 247 | memory_pointer_tr operator/(const uintptr_t& rhs) const 248 | { return memory_pointer_raw(this->a / rhs); } 249 | 250 | bool is_null() const { return this->p == nullptr; } 251 | uintptr_t as_int() const { return this->a; } 252 | 253 | #if __cplusplus >= 201103L 254 | explicit operator uintptr_t() const 255 | { return this->a; } 256 | #else 257 | #endif 258 | 259 | }; 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | /* 268 | * ProtectMemory 269 | * Makes the address @addr have a protection of @protection 270 | */ 271 | inline bool ProtectMemory(memory_pointer_tr addr, size_t size, DWORD protection) 272 | { 273 | return VirtualProtect(addr.get(), size, protection, &protection) != 0; 274 | } 275 | 276 | /* 277 | * UnprotectMemory 278 | * Unprotect the memory at @addr with size @size so it have all accesses (execute, read and write) 279 | * Returns the old protection to out_oldprotect 280 | */ 281 | inline bool UnprotectMemory(memory_pointer_tr addr, size_t size, DWORD& out_oldprotect) 282 | { 283 | return VirtualProtect(addr.get(), size, PAGE_EXECUTE_READWRITE, &out_oldprotect) != 0; 284 | } 285 | 286 | /* 287 | * scoped_unprotect 288 | * RAII wrapper for UnprotectMemory 289 | * On construction unprotects the memory, on destruction reprotects the memory 290 | */ 291 | struct scoped_unprotect 292 | { 293 | memory_pointer_raw addr; 294 | size_t size; 295 | DWORD dwOldProtect; 296 | bool bUnprotected; 297 | 298 | scoped_unprotect(memory_pointer_tr addr, size_t size) 299 | { 300 | if(size == 0) bUnprotected = false; 301 | else bUnprotected = UnprotectMemory(this->addr = addr.get(), this->size = size, dwOldProtect); 302 | } 303 | 304 | ~scoped_unprotect() 305 | { 306 | if(bUnprotected) ProtectMemory(this->addr.get(), this->size, this->dwOldProtect); 307 | } 308 | }; 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | /* 318 | * WriteMemoryRaw 319 | * Writes into memory @addr the content of @value with a sizeof @size 320 | * Does memory unprotection if @vp is true 321 | */ 322 | inline void WriteMemoryRaw(memory_pointer_tr addr, void* value, size_t size, bool vp) 323 | { 324 | scoped_unprotect xprotect(addr, vp? size : 0); 325 | memcpy(addr.get(), value, size); 326 | } 327 | 328 | /* 329 | * ReadMemoryRaw 330 | * Reads the memory at @addr with a sizeof @size into address @ret 331 | * Does memory unprotection if @vp is true 332 | */ 333 | inline void ReadMemoryRaw(memory_pointer_tr addr, void* ret, size_t size, bool vp) 334 | { 335 | scoped_unprotect xprotect(addr, vp? size : 0); 336 | memcpy(ret, addr.get(), size); 337 | } 338 | 339 | /* 340 | * MemoryFill 341 | * Fills the memory at @addr with the byte @value doing it @size times 342 | * Does memory unprotection if @vp is true 343 | */ 344 | inline void MemoryFill(memory_pointer_tr addr, uint8_t value, size_t size, bool vp) 345 | { 346 | scoped_unprotect xprotect(addr, vp? size : 0); 347 | memset(addr.get(), value, size); 348 | } 349 | 350 | /* 351 | * WriteObject 352 | * Assigns the object @value into the same object type at @addr 353 | * Does memory unprotection if @vp is true 354 | */ 355 | template 356 | inline T& WriteObject(memory_pointer_tr addr, const T& value, bool vp = false) 357 | { 358 | scoped_unprotect xprotect(addr, vp? sizeof(value) : 0); 359 | return (*addr.get() = value); 360 | } 361 | 362 | /* 363 | * ReadObject 364 | * Assigns the object @value with the value of the same object type at @addr 365 | * Does memory unprotection if @vp is true 366 | */ 367 | template 368 | inline T& ReadObject(memory_pointer_tr addr, T& value, bool vp = false) 369 | { 370 | scoped_unprotect xprotect(addr, vp? sizeof(value) : 0); 371 | return (value = *addr.get()); 372 | } 373 | 374 | 375 | /* 376 | * WriteMemory 377 | * Writes the object of type T into the address @addr 378 | * Does memory unprotection if @vp is true 379 | */ 380 | template 381 | inline void WriteMemory(memory_pointer_tr addr, T value, bool vp = false) 382 | { 383 | WriteObject(addr, value, vp); 384 | } 385 | 386 | /* 387 | * ReadMemory 388 | * Reads the object type T at address @addr 389 | * Does memory unprotection if @vp is true 390 | */ 391 | template 392 | inline T ReadMemory(memory_pointer_tr addr, bool vp = false) 393 | { 394 | T value; 395 | return ReadObject(addr, value, vp); 396 | } 397 | 398 | /* 399 | * AdjustPointer 400 | * Searches in the range [@addr, @addr + @max_search] for a pointer in the range [@default_base, @default_end] and replaces 401 | * it with the proper offset in the pointer @replacement_base. 402 | * Does memory unprotection if @vp is true. 403 | */ 404 | inline memory_pointer_raw AdjustPointer(memory_pointer_tr addr, 405 | memory_pointer_raw replacement_base, memory_pointer_tr default_base, memory_pointer_tr default_end, 406 | size_t max_search = 8, bool vp = true) 407 | { 408 | scoped_unprotect xprotect(addr, vp? max_search + sizeof(void*) : 0); 409 | for(size_t i = 0; i < max_search; ++i) 410 | { 411 | memory_pointer_raw ptr = ReadMemory(addr + i); 412 | if(ptr >= default_base.get() && ptr <= default_end.get()) 413 | { 414 | auto result = replacement_base + (ptr - default_base.get()); 415 | WriteMemory(addr + i, result.get()); 416 | return result; 417 | } 418 | } 419 | return nullptr; 420 | } 421 | 422 | 423 | 424 | 425 | 426 | 427 | /* 428 | * GetAbsoluteOffset 429 | * Gets absolute address based on relative offset @rel_value from instruction that ends at @end_of_instruction 430 | */ 431 | inline memory_pointer_raw GetAbsoluteOffset(int rel_value, memory_pointer_tr end_of_instruction) 432 | { 433 | return end_of_instruction.get() + rel_value; 434 | } 435 | 436 | /* 437 | * GetRelativeOffset 438 | * Gets relative offset based on absolute address @abs_value for instruction that ends at @end_of_instruction 439 | */ 440 | inline int GetRelativeOffset(memory_pointer_tr abs_value, memory_pointer_tr end_of_instruction) 441 | { 442 | return uintptr_t(abs_value.get() - end_of_instruction.get()); 443 | } 444 | 445 | /* 446 | * ReadRelativeOffset 447 | * Reads relative offset from address @at 448 | */ 449 | inline memory_pointer_raw ReadRelativeOffset(memory_pointer_tr at, size_t sizeof_addr = 4, bool vp = true) 450 | { 451 | switch(sizeof_addr) 452 | { 453 | case 1: return (GetAbsoluteOffset(ReadMemory (at, vp), at+sizeof_addr)); 454 | case 2: return (GetAbsoluteOffset(ReadMemory(at, vp), at+sizeof_addr)); 455 | case 4: return (GetAbsoluteOffset(ReadMemory(at, vp), at+sizeof_addr)); 456 | } 457 | return nullptr; 458 | } 459 | 460 | /* 461 | * MakeRelativeOffset 462 | * Writes relative offset into @at based on absolute destination @dest 463 | */ 464 | inline void MakeRelativeOffset(memory_pointer_tr at, memory_pointer_tr dest, size_t sizeof_addr = 4, bool vp = true) 465 | { 466 | switch(sizeof_addr) 467 | { 468 | case 1: WriteMemory (at, static_cast (GetRelativeOffset(dest, at+sizeof_addr)), vp); 469 | case 2: WriteMemory(at, static_cast(GetRelativeOffset(dest, at+sizeof_addr)), vp); 470 | case 4: WriteMemory(at, static_cast(GetRelativeOffset(dest, at+sizeof_addr)), vp); 471 | } 472 | } 473 | 474 | /* 475 | * GetBranchDestination 476 | * Gets the destination of a branch instruction at address @at 477 | * *** Works only with JMP and CALL for now *** 478 | */ 479 | inline memory_pointer_raw GetBranchDestination(memory_pointer_tr at, bool vp = true) 480 | { 481 | switch(ReadMemory(at, vp)) 482 | { 483 | // We need to handle other instructions (and prefixes) later... 484 | case 0xE8: // call rel 485 | case 0xE9: // jmp rel 486 | return ReadRelativeOffset(at + 1, 4, vp); 487 | 488 | case 0xFF: 489 | switch(ReadMemory(at + 1, vp)) 490 | { 491 | case 0x15: // call dword ptr [addr] 492 | case 0x25: // jmp dword ptr [addr] 493 | return *(ReadMemory(at + 2, vp)); 494 | } 495 | break; 496 | } 497 | return nullptr; 498 | } 499 | 500 | /* 501 | * MakeJMP 502 | * Creates a JMP instruction at address @at that jumps into address @dest 503 | * If there was already a branch instruction there, returns the previosly destination of the branch 504 | */ 505 | inline memory_pointer_raw MakeJMP(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) 506 | { 507 | auto p = GetBranchDestination(at, vp); 508 | WriteMemory(at, 0xE9, vp); 509 | MakeRelativeOffset(at+1, dest, 4, vp); 510 | return p; 511 | } 512 | 513 | /* 514 | * MakeCALL 515 | * Creates a CALL instruction at address @at that jumps into address @dest 516 | * If there was already a branch instruction there, returns the previosly destination of the branch 517 | */ 518 | inline memory_pointer_raw MakeCALL(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) 519 | { 520 | auto p = GetBranchDestination(at, vp); 521 | WriteMemory(at, 0xE8, vp); 522 | MakeRelativeOffset(at+1, dest, 4, vp); 523 | return p; 524 | } 525 | 526 | /* 527 | * MakeJA 528 | * Creates a JA instruction at address @at that jumps if above into address @dest 529 | * If there was already a branch instruction there, returns the previosly destination of the branch 530 | */ 531 | inline void MakeJA(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) 532 | { 533 | WriteMemory(at, 0x87F0, vp); 534 | MakeRelativeOffset(at+2, dest, 4, vp); 535 | } 536 | 537 | /* 538 | * MakeNOP 539 | * Creates a bunch of NOP instructions at address @at 540 | */ 541 | inline void MakeNOP(memory_pointer_tr at, size_t count = 1, bool vp = true) 542 | { 543 | MemoryFill(at, 0x90, count, vp); 544 | } 545 | 546 | /* 547 | * MakeRangedNOP 548 | * Creates a bunch of NOP instructions at address @at until address @until 549 | */ 550 | inline void MakeRangedNOP(memory_pointer_tr at, memory_pointer_tr until, bool vp = true) 551 | { 552 | return MakeNOP(at, size_t(until.get_raw() - at.get_raw()), vp); 553 | } 554 | 555 | 556 | /* 557 | * MakeRET 558 | * Creates a RET instruction at address @at popping @pop values from the stack 559 | * If @pop is equal to 0 it will use the 1 byte form of the instruction 560 | */ 561 | inline void MakeRET(memory_pointer_tr at, uint16_t pop = 0, bool vp = true) 562 | { 563 | WriteMemory(at, pop? 0xC2 : 0xC3, vp); 564 | if(pop) WriteMemory(at+1, pop, vp); 565 | } 566 | 567 | 568 | 569 | 570 | 571 | /* 572 | * lazy_pointer 573 | * Lazy pointer, where it's final value will get evaluated only once when finally needed. 574 | */ 575 | template 576 | struct lazy_pointer 577 | { 578 | public: 579 | // Returns the final raw pointer 580 | static auto_pointer get() 581 | { 582 | return xget().get(); 583 | } 584 | 585 | template 586 | static T* get() 587 | { 588 | return get().get(); 589 | } 590 | 591 | private: 592 | // Returns the final pointer 593 | static memory_pointer_raw xget() 594 | { 595 | static void* ptr = nullptr; 596 | if(!ptr) ptr = memory_pointer(addr).get(); 597 | return memory_pointer_raw(ptr); 598 | } 599 | }; 600 | 601 | /* 602 | * lazy_object 603 | * Lazy object, where it's final object will get evaluated only once when finally needed. 604 | */ 605 | template 606 | struct lazy_object 607 | { 608 | static T& get() 609 | { 610 | static T data; 611 | static bool has_data = false; 612 | if(!has_data) 613 | { 614 | ReadObject(addr, data, true); 615 | has_data = true; 616 | } 617 | return data; 618 | } 619 | }; 620 | 621 | 622 | /* 623 | Helpers 624 | */ 625 | 626 | template 627 | inline memory_pointer mem_ptr(T p) 628 | { 629 | return memory_pointer(p); 630 | } 631 | 632 | template 633 | inline memory_pointer_raw raw_ptr(T p) 634 | { 635 | return memory_pointer_raw(p); 636 | } 637 | 638 | template 639 | inline memory_pointer_raw raw_ptr(basic_memory_pointer p) 640 | { 641 | return raw_ptr(p.get()); 642 | } 643 | 644 | template 645 | inline memory_pointer_raw lazy_ptr() 646 | { 647 | return lazy_pointer::get(); 648 | } 649 | 650 | template 651 | inline memory_pointer_aslr aslr_ptr(T p) 652 | { 653 | return memory_pointer_aslr(p); 654 | } 655 | 656 | 657 | 658 | 659 | 660 | 661 | #ifndef INJECTOR_GVM_OWN_DETECT // Should we implement our detection method? 662 | 663 | // Detects game, region and version; returns false if could not detect it 664 | inline bool game_version_manager::Detect() 665 | { 666 | // Cleanup data 667 | this->Clear(); 668 | 669 | // Find NT header 670 | uintptr_t base = (uintptr_t) GetModuleHandleA(NULL); 671 | IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)(base); 672 | IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(base + dos->e_lfanew); 673 | 674 | // Look for game and version thought the entry-point 675 | // Thanks to Silent for many of the entry point offsets 676 | switch (base + nt->OptionalHeader.AddressOfEntryPoint + 0x400000 - (DWORD)GetModuleHandle(NULL)) 677 | { 678 | case 0x5C1E70: // GTA III 1.0 679 | game = '3', major = 1, minor = 0, region = 0, steam = false; 680 | return true; 681 | 682 | case 0x5C2130: // GTA III 1.1 683 | game = '3', major = 1, minor = 1, region = 0, steam = false; 684 | return true; 685 | 686 | case 0x5C6FD0: // GTA III 1.1 (Cracked Steam Version) 687 | case 0x9912ED: // GTA III 1.1 (Encrypted Steam Version) 688 | game = '3', major = 1, minor = 1, region = 0, steam = true; 689 | return true; 690 | 691 | case 0x667BF0: // GTA VC 1.0 692 | game = 'V', major = 1, minor = 0, region = 0, steam = false; 693 | return true; 694 | 695 | case 0x667C40: // GTA VC 1.1 696 | game = 'V', major = 1, minor = 1, region = 0, steam = false; 697 | return true; 698 | 699 | case 0x666BA0: // GTA VC 1.1 (Cracked Steam Version) 700 | case 0xA402ED: // GTA VC 1.1 (Encrypted Steam Version) 701 | game = 'V', major = 1, minor = 1, region = 0, steam = true; 702 | return true; 703 | 704 | case 0x82457C: // GTA SA 1.0 US Cracked 705 | case 0x824570: // GTA SA 1.0 US Compact 706 | game = 'S', major = 1, minor = 0, region = 'U', steam = false; 707 | cracker = injector::ReadMemory(raw_ptr(0x406A20), true) == 0xE9? 'H' : 0; 708 | return true; 709 | 710 | case 0x8245BC: // GTA SA 1.0 EU Cracked (??????) 711 | case 0x8245B0: // GTA SA 1.0 EU Cracked 712 | game = 'S', major = 1, minor = 0, region = 'E', steam = false; 713 | cracker = injector::ReadMemory(raw_ptr(0x406A20), true) == 0xE9? 'H' : 0; // just to say 'securom' 714 | return true; 715 | 716 | case 0x8252FC: // GTA SA 1.1 US Cracked 717 | game = 'S', major = 1, minor = 1, region = 'U', steam = false; 718 | return true; 719 | 720 | case 0x82533C: // GTA SA 1.1 EU Cracked 721 | game = 'S', major = 1, minor = 1, region = 'E', steam = false; 722 | return true; 723 | 724 | case 0x85EC4A: // GTA SA 3.0 (Cracked Steam Version) 725 | case 0xD3C3DB: // GTA SA 3.0 (Encrypted Steam Version) 726 | game = 'S', major = 3, minor = 0, region = 0, steam = true; 727 | return true; 728 | 729 | case 0xC965AD: // GTA IV 1.0.0.4 US 730 | game = 'I', major = 1, minor = 0, majorRevision = 0, minorRevision = 4, region = 'U', steam = false; 731 | return true; 732 | 733 | case 0xD0D011: // GTA IV 1.0.0.7 US 734 | game = 'I', major = 1, minor = 0, majorRevision = 0, minorRevision = 7, region = 'U', steam = false; 735 | return true; 736 | 737 | case 0xD0AF06: // GTA EFLC 1.1.2.0 US 738 | game = 'E', major = 1, minor = 1, majorRevision = 2, minorRevision = 0, region = 'U', steam = false; 739 | return true; 740 | 741 | default: 742 | return false; 743 | } 744 | } 745 | 746 | #endif 747 | 748 | 749 | } // namespace 750 | 751 | -------------------------------------------------------------------------------- /includes/injector/hooking.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Injectors - Classes for making your hooking life easy 3 | * 4 | * Copyright (C) 2013-2014 LINK/2012 5 | * 6 | * This software is provided 'as-is', without any express or implied 7 | * warranty. In no event will the authors be held liable for any damages 8 | * arising from the use of this software. 9 | * 10 | * Permission is granted to anyone to use this software for any purpose, 11 | * including commercial applications, and to alter it and redistribute it 12 | * freely, subject to the following restrictions: 13 | * 14 | * 1. The origin of this software must not be misrepresented; you must not 15 | * claim that you wrote the original software. If you use this software 16 | * in a product, an acknowledgment in the product documentation would be 17 | * appreciated but is not required. 18 | * 19 | * 2. Altered source versions must be plainly marked as such, and must not be 20 | * misrepresented as being the original software. 21 | * 22 | * 3. This notice may not be removed or altered from any source 23 | * distribution. 24 | * 25 | */ 26 | #pragma once 27 | #include "injector.hpp" 28 | #include 29 | #include 30 | #include // for std::shared_ptr 31 | #include 32 | 33 | namespace injector 34 | { 35 | /* 36 | * scoped_base 37 | * Base for any scoped hooking type 38 | * !!!! NOTICE !!!! --> Any derived which implements/reimplements restore() should implement a destructor calling it 39 | */ 40 | class scoped_base 41 | { 42 | public: 43 | virtual ~scoped_base() {} 44 | virtual void restore() = 0; 45 | }; 46 | 47 | /* 48 | * scoped_basic 49 | * Base for scoped types which will need a buffer to save/restore stuff 50 | */ 51 | template // TODO specialize bufsize=0 to be dynamic 52 | class scoped_basic : public scoped_base 53 | { 54 | private: 55 | uint8_t buf[bufsize];// Saved content 56 | memory_pointer_raw addr; // Data saved from this address 57 | size_t size; // Size saved 58 | bool saved; // Something saved? 59 | bool vp; // Virtual protect? 60 | 61 | public: 62 | 63 | static const bool is_dynamic = false; 64 | 65 | // Restore the previosly saved data 66 | // Problems may arise if someone else hooked the same place using the same method 67 | virtual void restore() 68 | { 69 | #ifndef INJECTOR_SCOPED_NOSAVE_NORESTORE 70 | if(this->saved) 71 | { 72 | WriteMemoryRaw(this->addr, this->buf, this->size, this->vp); 73 | this->saved = false; 74 | } 75 | #endif 76 | } 77 | 78 | // Save buffer at @addr with @size and virtual protect @vp 79 | virtual void save(memory_pointer_tr addr, size_t size, bool vp) 80 | { 81 | #ifndef INJECTOR_SCOPED_NOSAVE_NORESTORE 82 | assert(size <= bufsize); // Debug Safeness 83 | this->restore(); // Restore anything we have saved 84 | this->saved = true; // Mark that we have data save 85 | this->addr = addr.get(); // Save address 86 | this->size = size; // Save size 87 | this->vp = vp; // Save virtual protect 88 | ReadMemoryRaw(addr, buf, size, vp); // Save buffer 89 | #endif 90 | } 91 | 92 | public: 93 | // Constructor, initialises 94 | scoped_basic() : saved(false) 95 | {} 96 | 97 | ~scoped_basic() 98 | { 99 | this->restore(); 100 | } 101 | 102 | // No copy construction, we can't do this! Sure we can move construct :) 103 | scoped_basic(const scoped_basic&) = delete; 104 | scoped_basic(scoped_basic&& rhs) 105 | { 106 | *this = std::move(rhs); 107 | } 108 | 109 | scoped_basic& operator=(const scoped_basic& rhs) = delete; 110 | scoped_basic& operator=(scoped_basic&& rhs) 111 | { 112 | if(this->saved = rhs.saved) 113 | { 114 | assert(bufsize >= rhs.size); 115 | 116 | this->addr = rhs.addr; 117 | this->size = rhs.size; 118 | this->vp = rhs.vp; 119 | memcpy(buf, rhs.buf, rhs.size); 120 | 121 | rhs.saved = false; 122 | } 123 | return *this; 124 | } 125 | }; 126 | 127 | /* 128 | * RAII wrapper for memory writes 129 | * Can save only basic and POD types 130 | */ 131 | template 132 | class scoped_write : public scoped_basic 133 | { 134 | public: 135 | // Save buffer at @addr with @size and virtual protect @vp and then overwrite it with @value 136 | void write(memory_pointer_tr addr, void* value, size_t size, bool vp) 137 | { 138 | this->save(addr, size, vp); 139 | return WriteMemoryRaw(addr, value, size, vp); 140 | } 141 | 142 | // Save buffer at @addr with size sizeof(@value) and virtual protect @vp and then overwrite it with @value 143 | template 144 | void write(memory_pointer_tr addr, T value, bool vp = false) 145 | { 146 | this->save(addr, sizeof(T), vp); 147 | return WriteMemory(addr, value, vp); 148 | } 149 | 150 | // Constructors, move constructors, assigment operators........ 151 | scoped_write() = default; 152 | scoped_write(const scoped_write&) = delete; 153 | scoped_write(scoped_write&& rhs) : scoped_basic(std::move(rhs)) {} 154 | scoped_write& operator=(const scoped_write& rhs) = delete; 155 | scoped_write& operator=(scoped_write&& rhs) 156 | { scoped_basic::operator=(std::move(rhs)); return *this; } 157 | }; 158 | 159 | /* 160 | * RAII wrapper for filling 161 | */ 162 | template 163 | class scoped_fill : public scoped_basic 164 | { 165 | public: 166 | // Fills memory at @addr with value @value and size @size and virtual protect @vp 167 | void fill(memory_pointer_tr addr, uint8_t value, size_t size, bool vp) 168 | { 169 | this->save(addr, size, vp); 170 | return MemoryFill(addr, value, size, vp); 171 | } 172 | 173 | // Constructors, move constructors, assigment operators........ 174 | scoped_fill() = default; 175 | scoped_fill(const scoped_fill&) = delete; 176 | scoped_fill(scoped_fill&& rhs) : scoped_basic(std::move(rhs)) {} 177 | scoped_fill& operator=(const scoped_fill& rhs) = delete; 178 | scoped_fill& operator=(scoped_fill&& rhs) 179 | { scoped_basic::operator=(std::move(rhs)); return *this; } 180 | 181 | scoped_fill(memory_pointer_tr addr, uint8_t value, size_t size, bool vp) 182 | { fill(addr, value, vp); } 183 | }; 184 | 185 | /* 186 | * RAII wrapper for nopping 187 | */ 188 | template 189 | class scoped_nop : public scoped_basic 190 | { 191 | public: 192 | // Makes NOP at @addr with value @value and size @size and virtual protect @vp 193 | void make_nop(memory_pointer_tr addr, size_t size = 1, bool vp = true) 194 | { 195 | this->save(addr, size, vp); 196 | return MakeNOP(addr, size, vp); 197 | } 198 | 199 | // Constructors, move constructors, assigment operators........ 200 | scoped_nop() = default; 201 | scoped_nop(const scoped_nop&) = delete; 202 | scoped_nop(scoped_nop&& rhs) : scoped_basic(std::move(rhs)) {} 203 | scoped_nop& operator=(const scoped_nop& rhs) = delete; 204 | scoped_nop& operator=(scoped_nop&& rhs) 205 | { scoped_basic::operator=(std::move(rhs)); return *this; } 206 | 207 | scoped_nop(memory_pointer_tr addr, size_t size = 1, bool vp = true) 208 | { make_nop(addr, size, vp); } 209 | }; 210 | 211 | /* 212 | * RAII wrapper for MakeJMP 213 | */ 214 | class scoped_jmp : public scoped_basic<5> 215 | { 216 | public: 217 | // Makes NOP at @addr with value @value and size @size and virtual protect @vp 218 | memory_pointer_raw make_jmp(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) 219 | { 220 | this->save(at, 5, vp); 221 | return MakeJMP(at, dest, vp); 222 | } 223 | 224 | // Constructors, move constructors, assigment operators........ 225 | scoped_jmp() = default; 226 | scoped_jmp(const scoped_jmp&) = delete; 227 | scoped_jmp(scoped_jmp&& rhs) : scoped_basic<5>(std::move(rhs)) {} 228 | scoped_jmp& operator=(const scoped_jmp& rhs) = delete; 229 | scoped_jmp& operator=(scoped_jmp&& rhs) 230 | { scoped_basic<5>::operator=(std::move(rhs)); return *this; } 231 | 232 | scoped_jmp(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) 233 | { make_jmp(at, dest, vp); } 234 | }; 235 | 236 | /* 237 | * RAII wrapper for MakeCALL 238 | */ 239 | class scoped_call : public scoped_basic<5> 240 | { 241 | public: 242 | // Makes NOP at @addr with value @value and size @size and virtual protect @vp 243 | memory_pointer_raw make_call(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) 244 | { 245 | this->save(at, 5, vp); 246 | return MakeCALL(at, dest, vp); 247 | } 248 | 249 | // Constructors, move constructors, assigment operators........ 250 | scoped_call() = default; 251 | scoped_call(const scoped_call&) = delete; 252 | scoped_call(scoped_call&& rhs) : scoped_basic<5>(std::move(rhs)) {} 253 | scoped_call& operator=(const scoped_call& rhs) = delete; 254 | scoped_call& operator=(scoped_call&& rhs) 255 | { scoped_basic<5>::operator=(std::move(rhs)); return *this; } 256 | 257 | scoped_call(memory_pointer_tr at, memory_pointer_raw dest, bool vp = true) 258 | { make_call(at, dest, vp); } 259 | }; 260 | 261 | 262 | #if __cplusplus >= 201103L || _MSC_VER >= 1800 // C++11 or MSVC 2013 required for variadic templates 263 | 264 | /* 265 | * function_hooker_manager 266 | * Manages many function_hookers that points to the same address 267 | * The need for this function arises because otherwise we would only be able to allow one hook per address using function_hookers 268 | * This manager takes care of the amount of hooks placed in a particular address, calls the hooks and unhooks when necessary. 269 | */ 270 | template 271 | class function_hooker_manager : protected scoped_call 272 | { 273 | private: 274 | using func_type_raw = typename ToManage::func_type_raw; 275 | using func_type = typename ToManage::func_type; 276 | using functor_type = typename ToManage::functor_type; 277 | using assoc_type = std::list>; 278 | 279 | // Only construction is allowed... by myself ofcourse... 280 | function_hooker_manager() = default; 281 | function_hooker_manager(const function_hooker_manager&) = delete; 282 | function_hooker_manager(function_hooker_manager&&) = delete; 283 | 284 | // 285 | func_type_raw original; // Pointer to the original function we've replaced 286 | assoc_type assoc; // Association between owners of a hook and the hook (map) 287 | bool has_hooked = false; // Is the hook already in place? 288 | 289 | // Find assoc iterator for the content owned by 'owned' 290 | typename assoc_type::iterator find_assoc(const ToManage& owner) 291 | { 292 | for(auto it = assoc.begin(); it != assoc.end(); ++it) 293 | if(it->first == &owner) return it; 294 | return assoc.end(); 295 | } 296 | 297 | // Adds a new item to the association map (or override if already in the map) 298 | void add(const ToManage& hooker, functor_type functor) 299 | { 300 | auto it = find_assoc(hooker); 301 | if(it != assoc.end()) 302 | it->second = std::move(functor); 303 | else 304 | assoc.emplace_back(&hooker, std::move(functor)); 305 | } 306 | 307 | public: 308 | // Forwards the call to all the installed hooks 309 | static Ret call_hooks(Args&... args) 310 | { 311 | auto& manager = *instance(); 312 | 313 | if(manager.assoc.size() == 0) // This may be uncommon but may happen (?), no hook installed 314 | return manager.original(args...); 315 | 316 | // Functor for the original call 317 | func_type original = [&manager](Args... args) -> Ret { 318 | return manager.original(args...); 319 | }; 320 | 321 | if(manager.assoc.size() == 1) 322 | { 323 | // We have only one hook, just use it directly no need to go further in complexity 324 | auto& functor = manager.assoc.begin()->second; 325 | return functor(std::move(original), args...); 326 | } 327 | else 328 | { 329 | // Build a serie of functors which captures the previous functor sending it to the next functor, 330 | // that's what would happen if the hooks took place independent of the template staticness (AAAAAAA) 331 | func_type next = std::move(original); 332 | for(auto it = manager.assoc.begin(); it != manager.assoc.end(); ++it) 333 | { 334 | auto& functor = it->second; 335 | next = [functor, next](Args... args) -> Ret 336 | { 337 | return functor(next, args...); 338 | }; 339 | } 340 | return next(args...); 341 | } 342 | } 343 | 344 | public: 345 | 346 | // Installs a hook associated with the function_hooker 'hooker' which would call the specified 'functor' 347 | // We need an auxiliar function pointer 'ptr' (to abstract calling conventions) which should forward itself to ^call_hooks 348 | void make_call(const ToManage& hooker, functor_type functor, memory_pointer_raw ptr) 349 | { 350 | this->add(hooker, std::move(functor)); 351 | 352 | // Make sure we only hook this address for the manager once 353 | if(!this->has_hooked) 354 | { 355 | // (the following cast is needed for __thiscall functions) 356 | this->original = (func_type_raw) (void*) scoped_call::make_call(hooker.addr, ptr).get(); 357 | this->has_hooked = true; 358 | } 359 | } 360 | 361 | // Restores the state of the call we've replaced in the game code 362 | // All installed hooks gets uninstalled after this 363 | void restore() 364 | { 365 | if(this->has_hooked) 366 | { 367 | this->has_hooked = false; 368 | this->assoc.clear(); 369 | return scoped_call::restore(); 370 | } 371 | } 372 | 373 | // Replaces the hook associated with 'from' to be associated with 'to' 374 | // After this call the 'from' object has no association in this manager 375 | void replace(const ToManage& from, const ToManage& to) 376 | { 377 | auto it = find_assoc(from); 378 | if(it != assoc.end()) 379 | { 380 | auto functor = std::move(it->second); 381 | assoc.erase(it); 382 | this->add(to, std::move(functor)); 383 | } 384 | } 385 | 386 | // Removes the hook associated with the specified 'hooker' 387 | // If the number of hooks reaches zero after the remotion, a restore will take place 388 | void remove(const ToManage& hooker) 389 | { 390 | auto it = find_assoc(hooker); 391 | if(it != assoc.end()) 392 | { 393 | assoc.erase(it); 394 | if(assoc.size() == 0) this->restore(); 395 | } 396 | } 397 | 398 | // The instance of this specific manager 399 | // This work as a shared pointer to avoid the static destruction of the manager even when a static function_hooker 400 | // still wants to use it. 401 | static std::shared_ptr instance() 402 | { 403 | static auto fm_ptr = std::shared_ptr(new function_hooker_manager()); 404 | return fm_ptr; 405 | } 406 | 407 | }; 408 | 409 | 410 | /* 411 | * function_hooker_base 412 | * Base for any function_hooker, this class manages the relationship with the function hooker manager 413 | */ 414 | template 415 | class function_hooker_base : public scoped_base 416 | { 417 | public: 418 | static const uintptr_t addr = addr1; 419 | 420 | using func_type_raw = FuncType; 421 | using func_type = std::function; 422 | using functor_type = std::function; 423 | using manager_type = function_hooker_manager; 424 | 425 | public: 426 | // Constructors, move constructors, assigment operators........ 427 | function_hooker_base(const function_hooker_base&) = delete; 428 | function_hooker_base& operator=(const function_hooker_base& rhs) = delete; 429 | 430 | function_hooker_base() : manager(manager_type::instance()) 431 | {} 432 | 433 | // 434 | virtual ~function_hooker_base() 435 | { 436 | this->restore(); 437 | } 438 | 439 | // The move constructor should do a replace in the manager 440 | function_hooker_base(function_hooker_base&& rhs) 441 | : scoped_base(std::move(rhs)), has_call(rhs.has_call), 442 | manager(rhs.manager) // (don't move the manager!, every function_hooker should own one) 443 | { 444 | manager->replace(rhs, *this); 445 | } 446 | 447 | // The move assignment operator should also do a replace in the manager 448 | function_hooker_base& operator=(function_hooker_base&& rhs) 449 | { 450 | scoped_base::operator=(std::move(rhs)); 451 | manager->replace(rhs, *this); 452 | this->has_call = rhs.has_call; 453 | this->manager = rhs.manager; // (don't move the manager! every function_hooker should own one) 454 | return *this; 455 | } 456 | 457 | // Deriveds should implement a proper make_call (yeah it's virtual so derived-deriveds can do some fest) 458 | virtual void make_call(functor_type functor) = 0; 459 | 460 | // Restores the state of the call we've replaced in the game code 461 | virtual void restore() 462 | { 463 | this->has_call = false; 464 | manager->remove(*this); 465 | } 466 | 467 | // Checkers whether a hook is installed 468 | bool has_hooked() 469 | { 470 | return this->has_call; 471 | } 472 | 473 | private: 474 | bool has_call = false; // Has a hook installed? 475 | std::shared_ptr manager; // **EVERY** function_hooker should have a ownership over it's manager_type 476 | // this prevents the static destruction of the manager_type while it may be still needed. 477 | 478 | protected: // Forwarders to the function hooker manager 479 | 480 | void make_call(functor_type functor, memory_pointer_raw ptr) 481 | { 482 | this->has_call = true; 483 | manager->make_call(*this, std::move(functor), ptr); 484 | } 485 | 486 | static Ret call_hooks(Args&... a) 487 | { 488 | return manager_type::call_hooks(a...); 489 | } 490 | }; 491 | 492 | 493 | 494 | 495 | /* 496 | * function_hooker 497 | * For standard conventions (usually __cdecl) 498 | */ 499 | template 500 | struct function_hooker; 501 | 502 | template 503 | class function_hooker 504 | : public function_hooker_base 505 | { 506 | private: 507 | using base = function_hooker_base; 508 | 509 | // The hook caller 510 | static Ret call(Args... a) 511 | { 512 | return base::call_hooks(a...); 513 | } 514 | 515 | public: 516 | // Constructors, move constructors, assigment operators........ 517 | function_hooker() = default; 518 | function_hooker(const function_hooker&) = delete; 519 | function_hooker(function_hooker&& rhs) : base(std::move(rhs)) {} 520 | function_hooker& operator=(const function_hooker& rhs) = delete; 521 | function_hooker& operator=(function_hooker&& rhs) 522 | { base::operator=(std::move(rhs)); return *this; } 523 | 524 | // Makes the hook 525 | void make_call(typename base::functor_type functor) 526 | { 527 | return base::make_call(std::move(functor), raw_ptr(call)); 528 | } 529 | }; 530 | 531 | 532 | /* 533 | * function_hooker_stdcall 534 | * For stdcall conventions (__stdcall) 535 | */ 536 | template 537 | struct function_hooker_stdcall; 538 | 539 | template 540 | struct function_hooker_stdcall 541 | : public function_hooker_base 542 | { 543 | private: 544 | using base = function_hooker_base; 545 | 546 | // The hook caller 547 | static Ret __stdcall call(Args... a) 548 | { 549 | return base::call_hooks(a...); 550 | } 551 | 552 | public: 553 | // Constructors, move constructors, assigment operators........ 554 | function_hooker_stdcall() = default; 555 | function_hooker_stdcall(const function_hooker_stdcall&) = delete; 556 | function_hooker_stdcall(function_hooker_stdcall&& rhs) : base(std::move(rhs)) {} 557 | function_hooker_stdcall& operator=(const function_hooker_stdcall& rhs) = delete; 558 | function_hooker_stdcall& operator=(function_hooker_stdcall&& rhs) 559 | { base::operator=(std::move(rhs)); return *this; } 560 | 561 | // Makes the hook 562 | void make_call(typename base::functor_type functor) 563 | { 564 | return base::make_call(std::move(functor), raw_ptr(call)); 565 | } 566 | }; 567 | 568 | 569 | /* 570 | * function_hooker_fastcall 571 | * For fastcall conventions (__fastcall) 572 | */ 573 | template 574 | struct function_hooker_fastcall; 575 | 576 | template 577 | struct function_hooker_fastcall 578 | : public function_hooker_base 579 | { 580 | private: 581 | using base = function_hooker_base; 582 | 583 | // The hook caller 584 | static Ret __fastcall call(Args... a) 585 | { 586 | return base::call_hooks(a...); 587 | } 588 | 589 | public: 590 | // Constructors, move constructors, assigment operators........ 591 | function_hooker_fastcall() = default; 592 | function_hooker_fastcall(const function_hooker_fastcall&) = delete; 593 | function_hooker_fastcall(function_hooker_fastcall&& rhs) : base(std::move(rhs)) {} 594 | function_hooker_fastcall& operator=(const function_hooker_fastcall& rhs) = delete; 595 | function_hooker_fastcall& operator=(function_hooker_fastcall&& rhs) 596 | { base::operator=(std::move(rhs)); return *this; } 597 | 598 | // Makes the hook 599 | void make_call(typename base::functor_type functor) 600 | { 601 | return base::make_call(std::move(functor), raw_ptr(call)); 602 | } 603 | }; 604 | 605 | 606 | /* 607 | * function_hooker_thiscall 608 | * For thiscall conventions (__thiscall, class methods) 609 | */ 610 | template 611 | struct function_hooker_thiscall; 612 | 613 | template 614 | struct function_hooker_thiscall 615 | : public function_hooker_base 616 | { 617 | private: 618 | using base = function_hooker_base; 619 | 620 | // The hook caller 621 | static Ret __thiscall call(Args... a) 622 | { 623 | return base::call_hooks(a...); 624 | } 625 | 626 | public: 627 | // Constructors, move constructors, assigment operators........ 628 | function_hooker_thiscall() = default; 629 | function_hooker_thiscall(const function_hooker_thiscall&) = delete; 630 | function_hooker_thiscall(function_hooker_thiscall&& rhs) : base(std::move(rhs)) {} 631 | function_hooker_thiscall& operator=(const function_hooker_thiscall& rhs) = delete; 632 | function_hooker_thiscall& operator=(function_hooker_thiscall&& rhs) 633 | { base::operator=(std::move(rhs)); return *this; } 634 | 635 | // Makes the hook 636 | void make_call(typename base::functor_type functor) 637 | { 638 | return base::make_call(std::move(functor), raw_ptr(call)); 639 | } 640 | }; 641 | 642 | 643 | 644 | /******************* HELPERS ******************/ 645 | 646 | /* 647 | * Adds a hook to be alive for the entire program lifetime 648 | * That means the hook received will be alive until the program dies. 649 | * Note: Parameter must be a rvalue 650 | */ 651 | template inline 652 | T& add_static_hook(T&& hooker) 653 | { 654 | static std::list a; 655 | return *a.emplace(a.end(), std::move(hooker)); 656 | } 657 | 658 | /* 659 | * Makes a hook which is alive until it gets out of scope 660 | * 'T' must be any function_hooker object 661 | */ 662 | template inline 663 | T make_function_hook(F functor) 664 | { 665 | T a; 666 | a.make_call(std::move(functor)); 667 | return a; 668 | } 669 | 670 | /* 671 | * Makes a hook which is alive for the entire lifetime of this program 672 | * 'T' must be any function_hooker object 673 | */ 674 | template inline 675 | T& make_static_hook(F functor) 676 | { 677 | return add_static_hook(make_function_hook(std::move(functor))); 678 | } 679 | 680 | 681 | // TODO when we have access to C++14 add a make_function_hook, make_stdcall_function_hook, and so on 682 | // the problem behind implement it with C++11 is that lambdas cannot be generic and the first param of a hook is a functor pointing 683 | // to the previous call pointer 684 | 685 | #endif 686 | 687 | } 688 | --------------------------------------------------------------------------------