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;
}