diff --git a/SRTPluginProviderRER1/GameMemoryRER1.cs b/SRTPluginProviderRER1/GameMemoryRER1.cs index 52424a8..6786571 100644 --- a/SRTPluginProviderRER1/GameMemoryRER1.cs +++ b/SRTPluginProviderRER1/GameMemoryRER1.cs @@ -18,18 +18,41 @@ public class GameMemoryRER1 : IGameMemoryRER1 public GamePlayer Player { get => _player; set => _player = value; } internal GamePlayer _player; + public GameInventory PlayerInventory { get => _playerInventory; set => _playerInventory = value; } + internal GameInventory _playerInventory; + public GameEnemy[] EnemyHealth { get => _enemyHealth; set => _enemyHealth = value; } internal GameEnemy[] _enemyHealth; public GameEndResults EndResults { get => _endResults; set => _endResults = value; } internal GameEndResults _endResults; + public float IGT { get => _igt; set => _igt = value; } + internal float _igt; + public TimeSpan IGTTimeSpan { get { TimeSpan timespanIGT; + if (IGT >= 0f) + timespanIGT = TimeSpan.FromSeconds(IGT); + else + timespanIGT = new TimeSpan(); + + return timespanIGT; + } + } + + public string IGTFormattedString => IGTTimeSpan.ToString(IGT_TIMESPAN_STRING_FORMAT, CultureInfo.InvariantCulture); + + public TimeSpan SegmentTimeSpan + { + get + { + TimeSpan timespanIGT; + if (EndResults.ClearTime >= 0f) timespanIGT = TimeSpan.FromSeconds(EndResults.ClearTime); else @@ -39,6 +62,6 @@ public TimeSpan IGTTimeSpan } } - public string IGTFormattedString => IGTTimeSpan.ToString(IGT_TIMESPAN_STRING_FORMAT, CultureInfo.InvariantCulture); + public string SegmentFormattedString => SegmentTimeSpan.ToString(IGT_TIMESPAN_STRING_FORMAT, CultureInfo.InvariantCulture); } } diff --git a/SRTPluginProviderRER1/GameMemoryRER1Scanner.cs b/SRTPluginProviderRER1/GameMemoryRER1Scanner.cs index ded6783..6ff269f 100644 --- a/SRTPluginProviderRER1/GameMemoryRER1Scanner.cs +++ b/SRTPluginProviderRER1/GameMemoryRER1Scanner.cs @@ -7,7 +7,7 @@ namespace SRTPluginProviderRER1 { internal unsafe class GameMemoryRER1Scanner : IDisposable { - private readonly int MAX_ENTITIES = 3; + private readonly int MAX_ENTITIES = 16; // Variables private ProcessMemoryHandler memoryAccess; private GameMemoryRER1 gameMemoryValues; @@ -16,9 +16,10 @@ internal unsafe class GameMemoryRER1Scanner : IDisposable public int ProcessExitCode => (memoryAccess != null) ? memoryAccess.ProcessExitCode : 0; // Pointer Address Variables - private int pointerAddressHP; + private int pointerAddressPlayer; private int pointerAddressStats; private int pointerAddressEnemy; + private int pointerAddressIGT; // Pointer Classes private IntPtr BaseAddress { get; set; } @@ -26,6 +27,8 @@ internal unsafe class GameMemoryRER1Scanner : IDisposable private MultilevelPointer[] PointerEnemy { get; set; } private MultilevelPointer PointerHP { get; set; } private MultilevelPointer PointerStats { get; set; } + private MultilevelPointer PointerInventory { get; set; } + private MultilevelPointer PointerIGT { get; set; } internal GameMemoryRER1Scanner(Process process = null) { @@ -49,8 +52,10 @@ internal unsafe void Initialize(Process process) BaseAddress = NativeWrappers.GetProcessBaseAddress(pid, PInvoke.ListModules.LIST_MODULES_32BIT); // Bypass .NET's managed solution for getting this and attempt to get this info ourselves via PInvoke since some users are getting 299 PARTIAL COPY when they seemingly shouldn't. //POINTERS - PointerHP = new MultilevelPointer(memoryAccess, IntPtr.Add(BaseAddress, pointerAddressHP), 0x44, 0x10); + PointerHP = new MultilevelPointer(memoryAccess, IntPtr.Add(BaseAddress, pointerAddressPlayer), 0x44, 0x10); + PointerInventory = new MultilevelPointer(memoryAccess, IntPtr.Add(BaseAddress, pointerAddressPlayer), 0x44, 0xF8); PointerStats = new MultilevelPointer(memoryAccess, IntPtr.Add(BaseAddress, pointerAddressStats)); + PointerIGT = new MultilevelPointer(memoryAccess, IntPtr.Add(BaseAddress, pointerAddressIGT)); var position = 0; PointerEnemy = new MultilevelPointer[MAX_ENTITIES]; gameMemoryValues._enemyHealth = new GameEnemy[MAX_ENTITIES]; @@ -66,14 +71,17 @@ internal unsafe void Initialize(Process process) private void SelectPointerAddresses() { pointerAddressEnemy = 0xDE6184; - pointerAddressHP = 0xD6F394; + pointerAddressPlayer = 0xD6F394; pointerAddressStats = 0xD707E4; + pointerAddressIGT = 0xD70B60; } internal void UpdatePointers() { PointerHP.UpdatePointers(); + PointerInventory.UpdatePointers(); PointerStats.UpdatePointers(); + PointerIGT.UpdatePointers(); for (var i = 0; i < MAX_ENTITIES; i++) { PointerEnemy[i].UpdatePointers(); @@ -82,16 +90,21 @@ internal void UpdatePointers() internal unsafe IGameMemoryRER1 Refresh() { - // Rebecca - gameMemoryValues._player = PointerHP.Deref(0xE3C); + // Player + gameMemoryValues._player = PointerHP.Deref(0x0); + + // Player Inventory + gameMemoryValues._playerInventory = PointerInventory.Deref(0x0); // Game Statistics gameMemoryValues._endResults = PointerStats.Deref(0x0); - //Enemy HP + gameMemoryValues._igt = PointerIGT.DerefFloat(0x2C); + + // Enemy HP Array for (var i = 0; i < MAX_ENTITIES; i++) { - gameMemoryValues._enemyHealth[i] = PointerEnemy[i].Deref(0xE3C); + gameMemoryValues._enemyHealth[i] = PointerEnemy[i].Deref(0x0); } HasScanned = true; diff --git a/SRTPluginProviderRER1/IGameMemoryRER1.cs b/SRTPluginProviderRER1/IGameMemoryRER1.cs index ee9d030..7ed2784 100644 --- a/SRTPluginProviderRER1/IGameMemoryRER1.cs +++ b/SRTPluginProviderRER1/IGameMemoryRER1.cs @@ -10,11 +10,15 @@ public interface IGameMemoryRER1 string VersionInfo { get; } GamePlayer Player { get; set; } + GameInventory PlayerInventory { get; set; } GameEnemy[] EnemyHealth { get; set; } GameEndResults EndResults { get; set; } + float IGT { get; set; } TimeSpan IGTTimeSpan { get; } string IGTFormattedString { get; } + TimeSpan SegmentTimeSpan { get; } + string SegmentFormattedString { get; } } } \ No newline at end of file diff --git a/SRTPluginProviderRER1/Structs/GameStructs/GameEndResults.cs b/SRTPluginProviderRER1/Structs/GameStructs/GameEndResults.cs index 4d6ec38..70c7a1e 100644 --- a/SRTPluginProviderRER1/Structs/GameStructs/GameEndResults.cs +++ b/SRTPluginProviderRER1/Structs/GameStructs/GameEndResults.cs @@ -7,14 +7,13 @@ namespace SRTPluginProviderRER1.Structs.GameStructs public struct GameEndResults { - [FieldOffset(0x34)] private int rank; [FieldOffset(0x38)] private int rankScore; [FieldOffset(0x6B8)] private int shotsFired; [FieldOffset(0x6BC)] private int enemiesHit; [FieldOffset(0x6C0)] private int deaths; [FieldOffset(0x6DC)] private float clearTime; - public int Rank => rank; + public int Rank => (int)Math.Floor((decimal)rankScore / 1000); public int RankScore => rankScore; public int ShotsFired => shotsFired; public int EnemiesHit => enemiesHit; diff --git a/SRTPluginProviderRER1/Structs/GameStructs/GameEnemy.cs b/SRTPluginProviderRER1/Structs/GameStructs/GameEnemy.cs index 71239b3..3e9643e 100644 --- a/SRTPluginProviderRER1/Structs/GameStructs/GameEnemy.cs +++ b/SRTPluginProviderRER1/Structs/GameStructs/GameEnemy.cs @@ -1,38 +1,68 @@ -using System.Diagnostics; +using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.InteropServices; namespace SRTPluginProviderRER1.Structs.GameStructs { - [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x8)] - [DebuggerDisplay("{_DebuggerDisplay,nq}")] + [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0xE44)] public struct GameEnemy { - [FieldOffset(0x0)] private float currentHP; - [FieldOffset(0x4)] private float maximumHP; + [FieldOffset(0xE28)] private short id; + [FieldOffset(0xE3C)] private float currentHP; + [FieldOffset(0xE40)] private float maximumHP; - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public string _DebuggerDisplay - { - get - { - if (IsTrigger) - { - return string.Format("TRIGGER", CurrentHP, MaximumHP, Percentage); - } - else if (IsAlive) - { - return string.Format("{0} / {1} ({2:P1})", CurrentHP, MaximumHP, Percentage); - } - return "DEAD / DEAD (0%)"; - } - } + public short ID => id; + public string Name => Enemies.NamesList.ContainsKey(ID) ? string.Format("{0}: ", Enemies.NamesList[ID]) : ""; + public bool IsNaN(float hp) => hp.CompareTo(float.NaN) == 0; + public float CurrentHP => !IsNaN(currentHP) ? currentHP : 0f; + public float MaximumHP => !IsNaN(maximumHP) ? maximumHP : 0f; + public bool IsTrigger => !Enemies.NamesList.ContainsKey(ID); + public bool IsAlive => !IsTrigger && CurrentHP != 0; + public bool IsDamaged => IsAlive ? CurrentHP < MaximumHP : false; + public float Percentage => (IsAlive) ? CurrentHP / MaximumHP : 0f; + } - public float CurrentHP => currentHP; - public float MaximumHP => maximumHP; - public bool IsTrigger => MaximumHP <= 10f || MaximumHP > 100000f; - public bool IsNaN => MaximumHP.CompareTo(float.NaN) == 0; - public bool IsAlive => !IsNaN && !IsTrigger && CurrentHP <= MaximumHP; - public bool IsDamaged => IsAlive && CurrentHP < MaximumHP; - public float Percentage => ((IsAlive) ? CurrentHP / MaximumHP : 0f); + public class Enemies + { + public static Dictionary NamesList = new Dictionary() + { + { 0x1000, "Mutated Ooze" }, + { 0x1010, "Claw Ooze" }, + { 0x1020, "Shooter Ooze" }, + { 0x1030, "Explode Ooze" }, + { 0x1060, "Rachael Ooze" }, + { 0x1070, "Mutated Ooze" }, + { 0x1080, "Claw Ooze" }, + { 0x1090, "Shooter Ooze" }, + { 0x1200, "Sea Creeper" }, + { 0x1201, "Sea Creeper" }, + { 0x1210, "Mutated Dog" }, + { 0x1211, "Mutated Dog" }, + { 0x1220, "Fish" }, + { 0x1221, "Blowfish" }, + { 0x1230, "Green Hunter" }, + { 0x1240, "Invisible Hunter" }, + { 0x1300, "Scarmiglione" }, + { 0x1301, "Scarmiglione" }, + { 0x1310, "Beach Blob" }, + { 0x1320, "Beach Blob" }, + { 0x1330, "LowPoly Beach Blob" }, + { 0x1400, "Scragdead" }, + { 0x1401, "Scragdead" }, + { 0x1410, "Wall Blister" }, + { 0x1420, "Draghignazzo" }, + { 0x1421, "Draghignazzo" }, + { 0x1422, "Draghignazzo" }, + { 0x1430, "Malacoda" }, + { 0x1431, "Malacoda" }, + { 0x1440, "Jack" }, + { 0x1442, "Jack" }, + { 0x1450, "Jack" }, + { 0x1910, "Zenobia" }, + { 0x1911, "Zenobia" }, + { 0x1912, "Zenobia" }, + { 0x1913, "Zenobia" }, + { 0x1920, "Zenobia" } + }; } } \ No newline at end of file diff --git a/SRTPluginProviderRER1/Structs/GameStructs/GameInventory.cs b/SRTPluginProviderRER1/Structs/GameStructs/GameInventory.cs new file mode 100644 index 0000000..c01e72c --- /dev/null +++ b/SRTPluginProviderRER1/Structs/GameStructs/GameInventory.cs @@ -0,0 +1,35 @@ +using System.Runtime.InteropServices; + +namespace SRTPluginProviderRER1.Structs.GameStructs +{ + [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0xD0)] + + public struct GameInventory + { + [FieldOffset(0x40)] private uint handgunAmmo; + [FieldOffset(0x44)] private uint shotgunAmmo; + [FieldOffset(0x48)] private uint machineGunAmmo; + [FieldOffset(0x4C)] private uint rifleAmmo; + [FieldOffset(0x50)] private uint magnumAmmo; + + [FieldOffset(0xA0)] private uint handGrenade; + [FieldOffset(0xA4)] private uint shockGrenade; + [FieldOffset(0xA8)] private uint bowDecoy; + [FieldOffset(0xAC)] private uint pulseGrenade; + + [FieldOffset(0xC8)] private uint greenHerb; + [FieldOffset(0xCC)] private uint oldKey; + + public uint HandgunAmmo => handgunAmmo; + public uint ShotgunAmmo => shotgunAmmo; + public uint MachineGunAmmo => machineGunAmmo; + public uint RifleAmmo => rifleAmmo; + public uint MagnumAmmo => magnumAmmo; + public uint HandGrenade => handGrenade; + public uint ShockGrenade => shockGrenade; + public uint BOWDecoy => bowDecoy; + public uint PulseGrenade => pulseGrenade; + public uint GreenHerb => greenHerb; + public uint OldKey => oldKey; + } +} diff --git a/SRTPluginProviderRER1/Structs/GameStructs/GamePlayer.cs b/SRTPluginProviderRER1/Structs/GameStructs/GamePlayer.cs index 5cff377..a5a13a4 100644 --- a/SRTPluginProviderRER1/Structs/GameStructs/GamePlayer.cs +++ b/SRTPluginProviderRER1/Structs/GameStructs/GamePlayer.cs @@ -1,14 +1,18 @@ -using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Runtime.InteropServices; namespace SRTPluginProviderRER1.Structs.GameStructs { - [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x8)] + [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0xE44)] public struct GamePlayer { - [FieldOffset(0x0)] private float currentHP; - [FieldOffset(0x4)] private float maxHP; + [FieldOffset(0xE28)] private short id; + [FieldOffset(0xE3C)] private float currentHP; + [FieldOffset(0xE40)] private float maxHP; + public short ID => id; + public string Name => Characters.NamesList.ContainsKey(ID) ? string.Format("{0}: ", Characters.NamesList[ID]) : ""; public float CurrentHP => currentHP; public float MaxHP => maxHP; public float Percentage => CurrentHP > 0 ? (float)CurrentHP / (float)MaxHP : 0f; @@ -24,6 +28,41 @@ public PlayerState HealthState } } + public class Characters + { + public static Dictionary NamesList = new Dictionary() + { + { 0x1000, "Chris" }, + { 0x1010, "Jill" }, + { 0x1020, "Parker" }, + { 0x1030, "Jessica" }, + { 0x1040, "Keith" }, + { 0x1050, "Quint" }, + { 0x1060, "O'Brian" }, + { 0x1070, "Raymond" }, + { 0x1080, "Morgan" }, + { 0x1090, "Jack" }, + { 0x1100, "Chris" }, + { 0x1110, "Jill" }, + { 0x1120, "Parker" }, + { 0x1130, "Jessica" }, + { 0x1140, "Keith" }, + { 0x1150, "Quint" }, + { 0x1200, "Chris" }, + { 0x1210, "Jill" }, + { 0x1220, "Parker" }, + { 0x1230, "Jessica" }, + { 0x1240, "Keith" }, + { 0x1250, "Quint" }, + { 0x1300, "Chris" }, + { 0x1310, "Jill" }, + { 0x1320, "NoModel" }, + { 0x1330, "Jessica" }, + { 0x1400, "Chris" }, + { 0x1420, "Parker" } + }; + } + public enum PlayerState { Dead,