diff --git a/DemulShooter/DemulShooter.csproj b/DemulShooter/DemulShooter.csproj index 33ccb33..9cad19c 100644 --- a/DemulShooter/DemulShooter.csproj +++ b/DemulShooter/DemulShooter.csproj @@ -71,6 +71,7 @@ + diff --git a/DemulShooter/Game_LindberghHotd4.cs b/DemulShooter/Game_LindberghHotd4.cs index 5b72d64..2f6d6d0 100644 --- a/DemulShooter/Game_LindberghHotd4.cs +++ b/DemulShooter/Game_LindberghHotd4.cs @@ -18,7 +18,7 @@ class Game_LindberghHotd4 : Game private const string P1_WEAPONBTN_INIT_NOP_ADDRESS = "0x081C960D|3"; // Pointer address used to find the INPUT_SET struct containing both players data in game - private const int BASE_PLAYER_DATA_PTR_OFFSET = 0x0013BF8C; + private const int BASE_PLAYER_DATA_PTR_OFFSET = 0x007F621C; // INPUT_SET direct address private int _Base_Player_Data_Address = 0; // INPUT_SET offsets to find data @@ -81,11 +81,13 @@ private void tProcess_Tick(Object Sender, EventArgs e) { byte[] Buffer = ReadBytes((int)_TargetProcess_MemoryBaseAddress + BASE_PLAYER_DATA_PTR_OFFSET, 4); int i = BitConverter.ToInt32(Buffer, 0); - Buffer = ReadBytes(i + 0x300, 4); - i = BitConverter.ToInt32(Buffer, 0); - Buffer = ReadBytes(i, 4); + + Buffer = ReadBytes(i + 0x5C0, 4); i = BitConverter.ToInt32(Buffer, 0); + Buffer = ReadBytes(i + 0x00, 4); + i = BitConverter.ToInt32(Buffer, 0); + if (i != 0) { _Base_Player_Data_Address = i + 0x34; @@ -169,7 +171,7 @@ public override bool GameScale(MouseInfo Mouse, int Player) private void SetHack() { SetHack_GunInit(); - SetHack_GunMainProc(); + SetHackV2(); SetHackEnableP2(); WriteLog("Memory Hack complete !"); @@ -183,31 +185,30 @@ private void SetHack_GunInit() { SetNops(0, P1_X_INIT_NOP_ADDRESS); SetNops(0, P1_Y_INIT_NOP_ADDRESS); - SetNops(0, P1_TRIGGER_INIT_NOP_ADDRESS); - SetNops(0, P1_RELOAD_INIT_NOP_ADDRESS); - SetNops(0, P1_WEAPONBTN_INIT_NOP_ADDRESS); + //SetNops(0, P1_TRIGGER_INIT_NOP_ADDRESS); + //SetNops(0, P1_RELOAD_INIT_NOP_ADDRESS); + //SetNops(0, P1_WEAPONBTN_INIT_NOP_ADDRESS); } - // CGunMgr::MainProc() => 0x08152B4C ~~ 0x08153053 - // Called in a loop by CGunMgr::Main() [0x08152844 ~~ 0x08152B3C] - // Noping Axis and Buttons instructions after game start causes crash, so hacks are a little more specific - private void SetHack_GunMainProc() + private void SetHackV2() { - // At the beginning, Buttons are all set to 0 - // We are replace the offset byte of Trigger, Reload and Grenade - // with START button offset: mov [ebp+0x08], edi => mov [ebp+0x20], edi ----> (89 7D 08 => 89 7D 20) - WriteByte(0x08152B6B, 0x20); - WriteByte(0x08152B6E, 0x20); - WriteByte(0x08152B71, 0x20); - - // The procedures sets Axis values after reading JVS data - // Replacing a conditional Jump by a single Jump will force skipping Axis/Reload update (74 18 => EB 10) - WriteBytes(0x08152ED4, new byte[] {0xEB, 0x10}); - - // The procedures uses masks to test JVS bits - // Again, replacing conditionnal Jumps by single Jumps will skip updates for Trigger/Grenade (74 06 => EB 06) - WriteByte(0x08152F2F, 0xEB); - WriteByte(0x08152F49, 0xEB); + //Axis blocking + SetNops(0, "0x08152EDB|3"); + SetNops(0, "0x08152EE3|3"); + //Trigger + SetNops(0, "0x08152B69|3"); + //Weapon + SetNops(0, "0x08152B6F|3"); + //Reload + SetNops(0, "0x08152B6C|3"); + SetNops(0, "0x08152F00|7"); + WriteByte(_Base_Player_Data_Address + P1_RELOAD_OFFSET, 0x00); + WriteByte(_Base_Player_Data_Address + P2_RELOAD_OFFSET, 0x00); + //Init : center cursors for P1 and P2 + WriteByte(_Base_Player_Data_Address + P1_X_OFFSET, 0x7F); + WriteByte(_Base_Player_Data_Address + P1_Y_OFFSET, 0x7F); + WriteByte(_Base_Player_Data_Address + P2_X_OFFSET, 0x7F); + WriteByte(_Base_Player_Data_Address + P2_Y_OFFSET, 0x7F); } // amCreditIsEnough() => 0x0831D800 ~~ 0x0831D895 diff --git a/DemulShooter/Game_LindberghLgj.cs b/DemulShooter/Game_LindberghLgj.cs new file mode 100644 index 0000000..c9d531a --- /dev/null +++ b/DemulShooter/Game_LindberghLgj.cs @@ -0,0 +1,325 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Windows.Forms; +using System.Runtime.InteropServices; +using System.Text; +using System.Collections.Generic; + +namespace DemulShooter +{ + class Game_LindberghLgj : Game + { + private const int BUTTONS_INJECTION_ADDRESS = 0x0840F40C; + private const int BUTTONS_INJECTION_RETURN_ADDRESS = 0x0840F412; + + private const string AXIS_X_NOP_ADDRESS = "0x080A9AD2|8"; + private const string AXIS_Y_NOP_ADDRESS = "0x080A97FE|8"; + + private byte[] _AxisX_HexCode = new byte[] { 0xF3, 0x0F, 0x11, 0x83, 0x34, 0x01, 0x00, 0x00}; + + //Base PTR to find P1 & P2 float axis values + private const int PLAYER_AXIS_PTR_OFFSET = 0x00130824; + private int _Player1_Float_Axis_Address = 0; + private int _Player2_Float_Axis_Address = 0; + private float _P1_X_Float; + private float _P1_Y_Float; + private float _P2_X_Float; + private float _P2_Y_Float; + + //Base PTR to find Buttons values + //private const int BUTTONS_PTR_OFFSET = 0x0011ED08; + private int _Buttons_Address = 0x08BECB79; + + /// + /// Constructor + /// + /// public Naomi_Game(String DemulVersion, bool Verbose, bool DisableWindow) + public Game_LindberghLgj(string RomName, bool Verbose) + : base() + { + GetScreenResolution(); + + _RomName = RomName; + _VerboseEnable = Verbose; + _ProcessHooked = false; + _Target_Process_Name = "BudgieLoader"; + + _tProcess = new Timer(); + _tProcess.Interval = 500; + _tProcess.Tick += new EventHandler(tProcess_Tick); + _tProcess.Enabled = true; + _tProcess.Start(); + + WriteLog("Waiting for Lindbergh " + _RomName + " game to hook....."); + } + + /// + /// Timer event when looking for Process (auto-Hook and auto-close) + /// + private void tProcess_Tick(Object Sender, EventArgs e) + { + if (!_ProcessHooked) + { + try + { + Process[] processes = Process.GetProcessesByName(_Target_Process_Name); + if (processes.Length > 0) + { + _TargetProcess = processes[0]; + _ProcessHandle = _TargetProcess.Handle; + _TargetProcess_MemoryBaseAddress = _TargetProcess.MainModule.BaseAddress; + + if (_TargetProcess_MemoryBaseAddress != IntPtr.Zero) + { + //Reading the code to be sure that the game is fully loaded by the emulator before hacking it + bool GameLoaded = true; + byte[] AxisBuffer = ReadBytes(0x080A9AD2, 8); + + for (int k = 0; k < _AxisX_HexCode.Length; k++) + { + if (_AxisX_HexCode[k] != AxisBuffer[k]) + { + GameLoaded = false; + break; + } + } + + byte[] Buffer = ReadBytes((int)_TargetProcess_MemoryBaseAddress + PLAYER_AXIS_PTR_OFFSET, 4); + int i1 = BitConverter.ToInt32(Buffer, 0); + int i2 = i1; + + Buffer = ReadBytes(i1 + 0x51C, 4); + i1 = BitConverter.ToInt32(Buffer, 0); + Buffer = ReadBytes(i2 + 0xC4, 4); + i2 = BitConverter.ToInt32(Buffer, 0); + + Buffer = ReadBytes(i1 + 0x0, 4); + i1 = BitConverter.ToInt32(Buffer, 0); + Buffer = ReadBytes(i2 + 0x0, 4); + i2 = BitConverter.ToInt32(Buffer, 0); + + + + if (i1 != 0 && i2 != 0 && GameLoaded) + { + _Player1_Float_Axis_Address = i1 + 0x134; + _Player2_Float_Axis_Address = i2 + 0x134; + + WriteLog("Player1_Axis_Address = 0x" + _Player1_Float_Axis_Address.ToString("X8")); + WriteLog("Player2_Axis_Address = 0x" + _Player2_Float_Axis_Address.ToString("X8")); + + _ProcessHooked = true; + WriteLog("Attached to Process " + _Target_Process_Name + ".exe, ProcessHandle = " + _ProcessHandle); + WriteLog(_Target_Process_Name + ".exe = 0x" + _TargetProcess_MemoryBaseAddress.ToString("X8")); + + SetHack(); + } + } + } + } + catch + { + WriteLog("Error trying to hook " + _Target_Process_Name + ".exe"); + } + } + else + { + Process[] processes = Process.GetProcessesByName(_Target_Process_Name); + if (processes.Length <= 0) + { + _ProcessHooked = false; + _TargetProcess = null; + _ProcessHandle = IntPtr.Zero; + _TargetProcess_MemoryBaseAddress = IntPtr.Zero; + WriteLog(_Target_Process_Name + ".exe closed"); + Environment.Exit(0); + } + } + } + + #region Screen + + /// + /// Convert client area pointer location to Game speciffic data for memory injection + /// + public override bool GameScale(MouseInfo Mouse, int Player) + { + if (_ProcessHandle != IntPtr.Zero) + { + try + { + //Demul Window size + Win32.Rect TotalRes = new Win32.Rect(); + Win32.GetClientRect(_TargetProcess.MainWindowHandle, ref TotalRes); + int TotalResX = TotalRes.Right - TotalRes.Left; + int TotalResY = TotalRes.Bottom - TotalRes.Top; + + WriteLog("Game client window resolution (Px) = [ " + TotalResX + "x" + TotalResY + " ]"); + + //X => [-1 ; 1] float + //Y => [-1 ; 1] float + + float X_Value = (2.0f * Mouse.pTarget.X / TotalResX) - 1.0f; + float Y_Value = 1.0f - (2.0f * Mouse.pTarget.Y / TotalResY); + + if (X_Value < -1.0f) + X_Value = -1.0f; + if (Y_Value < -1.0f) + Y_Value = -1.0f; + if (X_Value > 1.0f) + X_Value = 1.0f; + if (Y_Value > 1.0f) + Y_Value = 1.0f; + + if (Player == 1) + { + _P1_X_Float = X_Value; + _P1_Y_Float = Y_Value; + } + else if (Player == 2) + { + _P2_X_Float = X_Value; + _P2_Y_Float = Y_Value; + } + return true; + } + catch (Exception ex) + { + WriteLog("Error scaling mouse coordonates to GameFormat : " + ex.Message.ToString()); + } + } + return false; + } + + #endregion + + #region MemoryHack + + private void SetHack() + { + SetHack_Buttons(); + SetHack_Axis(); + SetHackEnableP2(); + + WriteLog("Memory Hack complete !"); + WriteLog("-"); + } + + private void SetHack_Axis() + { + SetNops(0, AXIS_X_NOP_ADDRESS); + SetNops(0, AXIS_Y_NOP_ADDRESS); + } + + /// + /// Start and Trigger are on the same Byte, so we can't simply NOP, to keep Teknoparrot Start button working + /// + private void SetHack_Buttons() + { + Memory CaveMemory = new Memory(_TargetProcess, _TargetProcess.MainModule.BaseAddress); + CaveMemory.Open(); + CaveMemory.Alloc(0x800); + List Buffer = new List(); + //cmp edx, 0 + CaveMemory.Write_StrBytes("83 FA 00"); + //je Hack + CaveMemory.Write_StrBytes("0F 84 0E 00 00 00"); + //cmp edx, 0 + CaveMemory.Write_StrBytes("83 FA 04"); + //je Hack + CaveMemory.Write_StrBytes("0F 84 05 00 00 00"); + //je original code + CaveMemory.Write_StrBytes("E9 17 00 00 00"); + //Hack + //and al, 0xF0 + CaveMemory.Write_StrBytes("24 F0"); + //and [edx+08BECB79],FFFFFF0F + CaveMemory.Write_StrBytes("81 A2 79 CB BE 08 0F FF FF FF"); + //or [edx+08BECB79],al + CaveMemory.Write_StrBytes("08 82 79 CB BE 08"); + //jmp exit + CaveMemory.Write_StrBytes("E9 06 00 00 00"); + //OriginalCode + //mov [edx+08BECB79],al + CaveMemory.Write_StrBytes("88 82 79 CB BE 08"); + CaveMemory.Write_jmp(BUTTONS_INJECTION_RETURN_ADDRESS); + + WriteLog("Adding Buttons Codecave at : 0x" + CaveMemory.CaveAddress.ToString("X8")); + + //Injection de code + IntPtr ProcessHandle = _TargetProcess.Handle; + int bytesWritten = 0; + int jumpTo = 0; + jumpTo = CaveMemory.CaveAddress - BUTTONS_INJECTION_ADDRESS - 5; + Buffer = new List(); + Buffer.Add(0xE9); + Buffer.AddRange(BitConverter.GetBytes(jumpTo)); + Buffer.Add(0x90); + Win32.WriteProcessMemory((int)ProcessHandle, BUTTONS_INJECTION_ADDRESS, Buffer.ToArray(), Buffer.Count, ref bytesWritten); + } + + // amCreditIsEnough() => 0x084E4A2F ~~ 0x084E4AC4 + // Even though Freeplay is forced by TeknoParrot, this procedure always find "NO CREDITS" for P2 + // Replacing conditionnal Jump by single Jump force OK (for both players) + private void SetHackEnableP2() + { + WriteByte(0x84E4A56, 0xEB); + } + + public override void SendInput(MouseInfo mouse, int Player) + { + if (Player == 1) + { + //Write Axis + WriteBytes(_Player1_Float_Axis_Address, BitConverter.GetBytes(_P1_X_Float)); + WriteBytes(_Player1_Float_Axis_Address + 4, BitConverter.GetBytes(_P1_Y_Float)); + + //Inputs + if (mouse.button == Win32.RI_MOUSE_LEFT_BUTTON_DOWN) + { + Apply_OR_ByteMask(_Buttons_Address, 0x02); + } + else if (mouse.button == Win32.RI_MOUSE_LEFT_BUTTON_UP) + { + Apply_AND_ByteMask(_Buttons_Address, 0xFD); + } + else if (mouse.button == Win32.RI_MOUSE_MIDDLE_BUTTON_DOWN) + { + Apply_OR_ByteMask(_Buttons_Address, 0x01); + } + else if (mouse.button == Win32.RI_MOUSE_MIDDLE_BUTTON_UP) + { + Apply_AND_ByteMask(_Buttons_Address, 0xFE); + } + } + else if (Player == 2) + { + //Write Axis + WriteBytes(_Player2_Float_Axis_Address, BitConverter.GetBytes(_P2_X_Float)); + WriteBytes(_Player2_Float_Axis_Address + 4, BitConverter.GetBytes(_P2_Y_Float)); + + //Inputs + if (mouse.button == Win32.RI_MOUSE_LEFT_BUTTON_DOWN) + { + Apply_OR_ByteMask(_Buttons_Address + 4, 0x02); + } + else if (mouse.button == Win32.RI_MOUSE_LEFT_BUTTON_UP) + { + Apply_AND_ByteMask(_Buttons_Address + 4, 0xFD); + } + else if (mouse.button == Win32.RI_MOUSE_MIDDLE_BUTTON_DOWN) + { + Apply_OR_ByteMask(_Buttons_Address + 4, 0x01); + } + else if (mouse.button == Win32.RI_MOUSE_MIDDLE_BUTTON_UP) + { + Apply_AND_ByteMask(_Buttons_Address + 4, 0xFE); + } + } + } + + #endregion + } +} diff --git a/DemulShooter/Program.cs b/DemulShooter/Program.cs index 6171108..ea782aa 100644 --- a/DemulShooter/Program.cs +++ b/DemulShooter/Program.cs @@ -30,7 +30,7 @@ static void Main(string[] args) string[] _WindowsRoms = new string[] { "artdead", "hfa", "hfa2p", "hfa_s", "hfa2p_s", "hfss", "hfss2p", "hfss_s", "hfss2p_s", "hod2pc", "hod3pc", "reload" }; string[] _TTXRoms = new string[] { "sha", "eadp", "gattack4", "gsoz", "gsoz2p", "hmuseum", "hmuseum2", "mgungun2" }; string[] _GlobalVrRoms = new string[] { "aliens", "alienshasp", "farcry", "fearland" }; - string[] _LindberghRoms = new string[] { "hotd4" }; + string[] _LindberghRoms = new string[] { "hotd4", "lgj" }; string[] _RingWideRoms = new string[] { "sgg", "lgi", "lgi3d", "og", "sdr" }; string[] _ChihiroRoms = new string[] { "vcop3" }; string[] _WipRoms = new string[] { "bestate", "wartran", "bhapc"}; @@ -48,7 +48,7 @@ static void Main(string[] args) Console.WriteLine(""); Console.WriteLine(""); Console.WriteLine("DemulShooter v" + System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString()); - Console.WriteLine("Build date : February, 14th 2019"); + Console.WriteLine("Build date : March, 29th 2019"); Console.WriteLine(""); Console.WriteLine("usage : DemulShooter.exe -target=[target] -rom=[rom] [options]"); Console.WriteLine(""); @@ -100,8 +100,9 @@ static void Main(string[] args) Console.WriteLine(" aliens\t\tAliens Extermination Dehasped (2nd dump, x86 and x64, no need for VM)"); Console.WriteLine(" fearland\tFright Fear Land"); Console.WriteLine(""); - Console.WriteLine("Lindbergh roms :"); + Console.WriteLine("Lindbergh roms (TeknoParrot 1.93 only) :"); Console.WriteLine(" hotd4\t\tHouse of The Dead 4"); + Console.WriteLine(" lgj\t\tLet's Go Jungle"); Console.WriteLine(""); Console.WriteLine("Model2 roms :"); Console.WriteLine(" bel\t\tBehind Enemy Lines"); diff --git a/DemulShooter/Properties/AssemblyInfo.cs b/DemulShooter/Properties/AssemblyInfo.cs index 427d5ce..8f92d66 100644 --- a/DemulShooter/Properties/AssemblyInfo.cs +++ b/DemulShooter/Properties/AssemblyInfo.cs @@ -31,4 +31,4 @@ // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : -[assembly: AssemblyVersion("8.5.3.0")] +[assembly: AssemblyVersion("8.5.4.0")] diff --git a/DemulShooter/WndParam.cs b/DemulShooter/WndParam.cs index 1b13ff6..2b9dde8 100644 --- a/DemulShooter/WndParam.cs +++ b/DemulShooter/WndParam.cs @@ -423,6 +423,10 @@ public WndParam(bool VerboseEnable) { _Game = new Game_LindberghHotd4(_Rom.ToLower(), _VerboseEnable); } break; + case "lgj": + { + _Game = new Game_LindberghLgj(_Rom.ToLower(), _VerboseEnable); + } break; default: break; }