diff --git a/SekiroFpsUnlockAndMore/MainWindow.xaml.cs b/SekiroFpsUnlockAndMore/MainWindow.xaml.cs index 84ff670..5aa014b 100644 --- a/SekiroFpsUnlockAndMore/MainWindow.xaml.cs +++ b/SekiroFpsUnlockAndMore/MainWindow.xaml.cs @@ -11,7 +11,9 @@ using System.Windows.Threading; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text.RegularExpressions; - +using System.Timers; +using Timer = System.Timers.Timer; + namespace SekiroFpsUnlockAndMore { public partial class MainWindow : Window @@ -26,18 +28,27 @@ namespace SekiroFpsUnlockAndMore internal long _offset_resolution_default = 0x0; internal long _offset_resolution_scaling_fix = 0x0; internal long _offset_fovsetting = 0x0; + //game stat offsets + internal long _offset_player_deaths = 0x0; + internal long _pointer_player_deaths = 0x0; internal long _offset_timescale = 0x0; internal long _offset_timescale_player = 0x0; internal bool _use_resolution_720 = false; + internal long _offset_total_kills = 0x0; + internal long _pointer_total_kills = 0x0; + internal const string deathCounterFilename = "DeathCouner.txt"; internal SettingsService _settingsService; - internal readonly DispatcherTimer _dispatcherTimerCheck = new DispatcherTimer(); + internal const string totalKillsFilename = "TotalKillsCounter.txt"; + + internal readonly Timer _statRecordTimer = new Timer(); + internal readonly DispatcherTimer _dispatcherTimerCheck = new DispatcherTimer(); internal bool _running = false; internal string _logPath; internal bool _retryAccess = true; internal RECT _windowRect; - public MainWindow() + public MainWindow() { InitializeComponent(); } @@ -75,7 +86,10 @@ namespace SekiroFpsUnlockAndMore }); _dispatcherTimerCheck.Interval = new TimeSpan(0, 0, 0, 2); _dispatcherTimerCheck.Start(); - } + + _statRecordTimer.Elapsed += new ElapsedEventHandler(StatReadTimer); + _statRecordTimer.Interval = 1500; + } /// /// On window closing. @@ -88,7 +102,9 @@ namespace SekiroFpsUnlockAndMore UnregisterHotKey(hWnd, 9009); if (_gameAccessHwnd != IntPtr.Zero) CloseHandle(_gameAccessHwnd); - } + + _statRecordTimer.Stop(); + } /// /// Windows Message queue (Wndproc) to catch HotKeyPressed @@ -288,6 +304,29 @@ namespace SekiroFpsUnlockAndMore this.cbFov.IsEnabled = false; } + //Game stats + _offset_player_deaths = patternScan.FindPatternInternal(_gameAccessHwndStatic, procList[gameIndex].MainModule, GameData.PATTERN_PLAYER_DEATHS, GameData.PATTERN_PLAYER_DEATHS_MASK, ' '); + Debug.WriteLine("Player Deaths found at: 0x" + _offset_player_deaths.ToString("X")); + if (!IsValidAddress(_offset_player_deaths)) + { + LogToFile("Player death counter not found..."); + } + else + { + _pointer_player_deaths = Read(_gameAccessHwndStatic, DereferenceStaticX64Pointer(_gameAccessHwndStatic, _offset_player_deaths, 0)) + 0x90; + } + + _offset_total_kills = patternScan.FindPatternInternal(_gameProc, procList[gameIndex].MainModule, GameData.PATTERN_TOTAL_KILLS, GameData.PATTERN_TOTAL_KILLS_MASK, ' ') + GameData.PATTERN_TOTAL_KILLS_OFFSET; + Debug.WriteLine("Total kills found at: 0x" + _offset_total_kills.ToString("X")); + if (!IsValidAddress(_offset_total_kills)) + { + LogToFile("Total kills counter not found..."); + } + else + { + _pointer_total_kills = DereferenceStaticX64Pointer(_gameAccessHwndStatic, _offset_total_kills, 0); + } + this.cbBorderless.IsEnabled = true; long offset_pTimeRelated = patternScan.FindPatternInternal(GameData.PATTERN_TIMESCALE, GameData.PATTERN_TIMESCALE_MASK, ' '); @@ -353,7 +392,7 @@ namespace SekiroFpsUnlockAndMore this.bPatch.IsEnabled = true; - _running = true; + _running = true; _dispatcherTimerCheck.Stop(); return Task.FromResult(true); } @@ -655,11 +694,32 @@ namespace SekiroFpsUnlockAndMore UpdateStatus(DateTime.Now.ToString("HH:mm:ss") + " Game unpatched!", Brushes.White); } - /// - /// Returns the hexadecimal representation of an IEEE-754 floating point number - /// - /// The floating point number. - /// The hexadecimal representation of the input. + /// + /// Reads some hidden stats and outputs them to text files. Use to display counters on Twitch stream or just look at them and get disspointed + /// (_gameAccessHwndStatic, _pointer_player_deaths); + //Debug.WriteLine("[STAT]Player deaths: " + playerDeaths); + LogStatFile(deathCounterFilename, playerDeaths.ToString()); + + if (IsValidAddress(_pointer_total_kills)) + { + int totalKills = Read(_gameAccessHwndStatic, _pointer_total_kills); + totalKills -= playerDeaths; //Since this value seems to track every death, including the player + //Debug.WriteLine("[STAT]Enemies killed: " + totalKills); + LogStatFile(totalKillsFilename, totalKills.ToString()); + } + } + } + + /// + /// Returns the hexadecimal representation of an IEEE-754 floating point number + /// + /// The floating point number. + /// The hexadecimal representation of the input. private static string GetHexRepresentationFromFloat(float input) { uint f = BitConverter.ToUInt32(BitConverter.GetBytes(input), 0); @@ -848,7 +908,27 @@ namespace SekiroFpsUnlockAndMore } } - private void UpdateStatus(string text, Brush color) + /// + /// Logs stat values to separate files for the use in OBS + /// + /// File name + /// Just a single stat value + private void LogStatFile(string filename, string value) + { + try + { + using (StreamWriter writer = new StreamWriter(filename, false)) + { + writer.Write(value); + } + } + catch (Exception ex) + { + MessageBox.Show("Failed writing stat file: " + ex.Message, "Sekiro Fps Unlock And More"); + } + } + + private void UpdateStatus(string text, Brush color) { this.tbStatus.Background = color; this.tbStatus.Text = text; @@ -985,7 +1065,12 @@ namespace SekiroFpsUnlockAndMore if (this.cbPlayerSpeed.IsChecked == true) PatchPlayerSpeed(); } - private void BPatch_Click(object sender, RoutedEventArgs e) + private void CbStatChanged(object sender, RoutedEventArgs e) + { + _statRecordTimer.Enabled = (bool)cbLogStats.IsChecked; + } + + private void BPatch_Click(object sender, RoutedEventArgs e) { PatchGame(); }