diff --git a/Dakar18Trainer/Cheats/NoSpeedLimitCheat.cs b/Dakar18Trainer/Cheats/NoSpeedLimitCheat.cs index 17aa5ae..db6df77 100644 --- a/Dakar18Trainer/Cheats/NoSpeedLimitCheat.cs +++ b/Dakar18Trainer/Cheats/NoSpeedLimitCheat.cs @@ -11,15 +11,13 @@ public class NoSpeedLimitCheat: BaseCheat { private static readonly int[] SPEED_LIMIT_OFFSETS = { 0x04194850, 0x28, 0x1A0, 0x1B0, 0xA0, 0x20, 0x20, 0x830, 0x3A8 }; - public override string name { get; } = "No speed limits"; + public override string name { get; } = "No speed limit"; public override Combination keyboardShortcut { get; } = Combination.TriggeredBy(Keys.L).Control().Alt(); - public override void applyIfNecessary(ProcessHandle processHandle, MemoryEditor memoryEditor) { - if (!isEnabled.Value) return; - + protected override void apply(ProcessHandle processHandle) { IndirectMemoryAddress speedLimitAddress = new(processHandle, null, SPEED_LIMIT_OFFSETS); - memoryEditor.writeToProcessMemory(processHandle, speedLimitAddress, 9999); + MemoryEditor.writeToProcessMemory(processHandle, speedLimitAddress, 9999); } } diff --git a/Dakar18Trainer/Dakar18Trainer.csproj b/Dakar18Trainer/Dakar18Trainer.csproj index 489fad1..c3f9019 100644 --- a/Dakar18Trainer/Dakar18Trainer.csproj +++ b/Dakar18Trainer/Dakar18Trainer.csproj @@ -14,6 +14,7 @@ 4 true true + false AnyCPU diff --git a/Dakar18Trainer/Properties/AssemblyInfo.cs b/Dakar18Trainer/Properties/AssemblyInfo.cs index bb7b9c0..e76526c 100644 --- a/Dakar18Trainer/Properties/AssemblyInfo.cs +++ b/Dakar18Trainer/Properties/AssemblyInfo.cs @@ -5,11 +5,11 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("Dakar 18 v.13 +1 Trainer")] -[assembly: AssemblyDescription("Dakar 18 v.13 +1 Trainer")] +[assembly: AssemblyTitle("Dakar 18 v.13 +1 Trainer by Ben")] +[assembly: AssemblyDescription("Dakar 18 v.13 +1 Trainer by Ben")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Ben Hutchison")] -[assembly: AssemblyProduct("Dakar 18 v.13 +1 Trainer")] +[assembly: AssemblyProduct("Dakar 18 v.13 +1 Trainer by Ben")] [assembly: AssemblyCopyright("© 2021 Ben Hutchison")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..2864872 --- /dev/null +++ b/Readme.md @@ -0,0 +1,38 @@ +Trainers +=== + + +### Requirements +- [.NET Framework 4.7.2 runtime](https://dotnet.microsoft.com/download/dotnet-framework) or later (included in Windows 10 version 1803 and later) + +### Games + + +- [Dakar 18](#dakar-18) +- [Superhot: Mind Control Delete](#superhot-mind-control-delete) + + + + +## Dakar 18 + +![trainer screenshot](https://i.imgur.com/ZMcCTAs.png) + +[💾 Download](https://github.com/Aldaviva/Trainers/releases/download/1.0.1/Dakar18Trainer.exe) + +- No speed limit + - All speed limit warnings and penalties are removed in all difficulty levels. + - You are still automatically limited to 30 km/h in passage control auto-driving zones. + +*Supports Dakar 18 v.13, released in March 2019, which contains Desafio Ruta 40 Rally and Inca Rally* + + +## Superhot: Mind Control Delete + +![trainer screenshot](https://i.imgur.com/yH0Msy6.png) + +[💾 Download](https://github.com/Aldaviva/Trainers/releases/download/1.0.1/SuperhotMindControlDeleteTrainer.exe) + +- Infinite health + +*Supports `SHMCD.exe` 1.0.0, which has file version 2018.4.5.14584* diff --git a/SuperhotMindControlDeleteTrainer/Cheats/InfiniteHealthCheat.cs b/SuperhotMindControlDeleteTrainer/Cheats/InfiniteHealthCheat.cs index c66a8eb..79bef35 100644 --- a/SuperhotMindControlDeleteTrainer/Cheats/InfiniteHealthCheat.cs +++ b/SuperhotMindControlDeleteTrainer/Cheats/InfiniteHealthCheat.cs @@ -19,18 +19,16 @@ public class InfiniteHealthCheat: BaseCheat { public override Combination keyboardShortcut { get; } = Combination.TriggeredBy(Keys.H).Alt().Control(); - public override void applyIfNecessary(ProcessHandle processHandle, MemoryEditor memoryEditor) { - if (!isEnabled.Value) return; - - FixedMemoryAddress currentHeartsAddress = new(new IndirectMemoryAddress(processHandle, MODULE_NAME, CURRENT_HEARTS_OFFSETS).address); + protected override void apply(ProcessHandle processHandle) { + FixedMemoryAddress currentHeartsAddress = new(new IndirectMemoryAddress(processHandle, MODULE_NAME, CURRENT_HEARTS_OFFSETS).address); //used twice, so don't reevaluate address IndirectMemoryAddress maxHeartsAddress = new(processHandle, MODULE_NAME, MAX_HEARTS_OFFSETS); - int currentHearts = memoryEditor.readFromProcessMemory(processHandle, currentHeartsAddress); - int maxHearts = memoryEditor.readFromProcessMemory(processHandle, maxHeartsAddress); + int currentHearts = MemoryEditor.readFromProcessMemory(processHandle, currentHeartsAddress); + int maxHearts = MemoryEditor.readFromProcessMemory(processHandle, maxHeartsAddress); if (currentHearts < maxHearts) { Trace.WriteLine($"Setting health to {maxHearts:N0}..."); - memoryEditor.writeToProcessMemory(processHandle, currentHeartsAddress, maxHearts); + MemoryEditor.writeToProcessMemory(processHandle, currentHeartsAddress, maxHearts); } } diff --git a/SuperhotMindControlDeleteTrainer/Properties/AssemblyInfo.cs b/SuperhotMindControlDeleteTrainer/Properties/AssemblyInfo.cs index 5e43c1f..1272367 100644 --- a/SuperhotMindControlDeleteTrainer/Properties/AssemblyInfo.cs +++ b/SuperhotMindControlDeleteTrainer/Properties/AssemblyInfo.cs @@ -5,11 +5,11 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("Superhot: Mind Control Delete 1.0.0 +1 Trainer")] -[assembly: AssemblyDescription("Superhot: Mind Control Delete 1.0.0 +1 Trainer")] +[assembly: AssemblyTitle("Superhot: Mind Control Delete 1.0.0 +1 Trainer by Ben")] +[assembly: AssemblyDescription("Superhot: Mind Control Delete 1.0.0 +1 Trainer by Ben")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Ben Hutchison")] -[assembly: AssemblyProduct("Superhot: Mind Control Delete 1.0.0 +1 Trainer")] +[assembly: AssemblyProduct("Superhot: Mind Control Delete 1.0.0 +1 Trainer by Ben")] [assembly: AssemblyCopyright("© 2021 Ben Hutchison")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -47,5 +47,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("1.0.1.0")] +[assembly: AssemblyFileVersion("1.0.1.0")] \ No newline at end of file diff --git a/TrainerCommon/App/CombinationConverter.cs b/TrainerCommon/App/CombinationConverter.cs index 7db272a..168add3 100644 --- a/TrainerCommon/App/CombinationConverter.cs +++ b/TrainerCommon/App/CombinationConverter.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Windows.Data; @@ -11,15 +12,23 @@ namespace TrainerCommon.App { public class CombinationConverter: IValueConverter { + private static readonly IComparer HOTKEY_SORTER = new HotkeySorter(); + + /// + /// Like Gma.System.MouseKeyHook.Combination.ToString() but it prints Ctrl instead of Control like a normal menu hotkey + /// public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is Combination combination) { - // Like Gma.System.MouseKeyHook.Combination.ToString() but it prints Ctrl instead of Control like a normal menu hotkey - return string.Join("+", combination.Chord.Select(key => key switch { - Keys.Control => "Ctrl", - _ => key.ToString() - }).Append(combination.TriggerKey.ToString())); + IEnumerable modifiers = combination.Chord + .OrderBy(key => key, HOTKEY_SORTER) + .Select(key => key switch { + Keys.Control => "Ctrl", + Keys.LWin or Keys.RWin => "Win", + _ => key.ToString() + }); + return string.Join("+", modifiers.Append(combination.TriggerKey.ToString())); } else { - throw new ArgumentException("value is not an instance of Combination", nameof(value)); + throw new ArgumentException($"value {value} is not an instance of Combination", nameof(value)); } } @@ -27,6 +36,23 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu throw new NotImplementedException(); } + /// + /// Sort Shift after Ctrl and Alt, since this is how hotkeys appear in menus in Windows. + /// + private class HotkeySorter: IComparer { + + public int Compare(Keys x, Keys y) { + if (x is Keys.Shift && y is Keys.Alt or Keys.Control or Keys.LWin or Keys.RWin) { + return 1; + } else if (y is Keys.Shift && x is Keys.Alt or Keys.Control or Keys.LWin or Keys.RWin) { + return -1; + } else { + return x.CompareTo(y); + } + } + + } + } } \ No newline at end of file diff --git a/TrainerCommon/App/MainWindow.xaml b/TrainerCommon/App/MainWindow.xaml index 6c0a51e..0c59be5 100644 --- a/TrainerCommon/App/MainWindow.xaml +++ b/TrainerCommon/App/MainWindow.xaml @@ -3,13 +3,12 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:trainerCommon="clr-namespace:TrainerCommon" xmlns:app="clr-namespace:TrainerCommon.App" mc:Ignorable="d" - d:DataContext="{d:DesignInstance Type=trainerCommon:MainWindowViewModel, IsDesignTimeCreatable=True}" + d:DataContext="{d:DesignInstance Type=app:MainWindowViewModel, IsDesignTimeCreatable=True}" Title="{Binding windowTitle}" - Height="Auto" - Width="275" + Height="Auto" + Width="275" ResizeMode="CanMinimize" WindowStartupLocation="CenterScreen" SizeToContent="Height"> @@ -29,65 +28,30 @@ - - - - - - - - - - - + - - - - - - - + + - - - - - - + + + + + + + + - - - - - + + \ No newline at end of file diff --git a/TrainerCommon/App/MainWindowViewModel.cs b/TrainerCommon/App/MainWindowViewModel.cs index 7acd40e..f13fa84 100644 --- a/TrainerCommon/App/MainWindowViewModel.cs +++ b/TrainerCommon/App/MainWindowViewModel.cs @@ -1,7 +1,6 @@ -using System.Collections.Generic; -using System.Linq; +using System; +using System.Collections.Generic; using System.Threading; -using System.Windows.Forms; using Gma.System.MouseKeyHook; using KoKo.Property; using TrainerCommon.Cheats; @@ -18,7 +17,7 @@ public class MainWindowViewModel { public Game game { get; } - public string windowTitle => $"{game.name} {game.supportedVersion} +{game.cheats.Count:N0} Trainer"; + public string windowTitle => $"{game.name} {game.supportedVersion} +{game.cheats.Count:N0} Trainer by Ben"; // ReSharper disable once UnusedMember.Global - used by design time DataContext DesignInstance public MainWindowViewModel(): this(new SampleGame(), new TrainerServiceImpl()) { } @@ -26,8 +25,14 @@ public class MainWindowViewModel { public MainWindowViewModel(Game game, TrainerService trainerService) { this.game = game; - statusBarAttachmentMessage = DerivedProperty.Create(trainerService.isAttachedToGame, attached => - attached ? "Attached to game process" : "Detached from game process"); + statusBarAttachmentMessage = DerivedProperty.Create(trainerService.isAttachedToGame, attached => attached switch { + AttachmentState.TRAINER_STOPPED => "Attaching to game…", + AttachmentState.PROGRAM_NOT_RUNNING => "Waiting for game to start", + AttachmentState.MEMORY_ADDRESS_NOT_FOUND => "Attached, memory address not found", + AttachmentState.MEMORY_ADDRESS_COULD_NOT_BE_READ => "Attached, memory unreadable", + AttachmentState.ATTACHED => "Attached to game process", + _ => throw new ArgumentOutOfRangeException(nameof(attached), attached, null) + }); statusBarAttachmentMessage.EventSynchronizationContext = SynchronizationContext.Current; foreach (Cheat cheat in game.cheats) { @@ -35,19 +40,31 @@ public MainWindowViewModel(Game game, TrainerService trainerService) { } } + /// + /// For design time + /// private class SampleGame: Game { public string name { get; } = "My Game"; public string processName { get; } = "mygame.exe"; public string supportedVersion { get; } = "1.0.0"; - public IList cheats { get; } = Enumerable.Repeat(new SampleCheat(), 2).Cast().ToList(); + + public IList cheats { get; } = new List { + new SampleCheat("Infinite ammo", "Control+Alt+LWin+A"), + new SampleCheat("Infinite lives", "Shift+Control+Alt+L") + }; private class SampleCheat: Cheat { - public string name { get; set; } = "Infinite ammo"; - public Combination keyboardShortcut { get; set; } = Combination.TriggeredBy(Keys.A).Alt().Control(); + public string name { get; } + public Combination keyboardShortcut { get; } public SettableProperty isEnabled { get; } = new StoredProperty(); - public void applyIfNecessary(ProcessHandle processHandle, MemoryEditor memoryEditor) { } + public void applyIfNecessary(ProcessHandle processHandle) { } + + public SampleCheat(string name, string keyBoardShortcut) { + this.name = name; + keyboardShortcut = Combination.FromString(keyBoardShortcut); + } } diff --git a/TrainerCommon/Cheats/Cheat.cs b/TrainerCommon/Cheats/Cheat.cs index 953d3ff..5ede133 100644 --- a/TrainerCommon/Cheats/Cheat.cs +++ b/TrainerCommon/Cheats/Cheat.cs @@ -14,7 +14,7 @@ public interface Cheat { SettableProperty isEnabled { get; } - void applyIfNecessary(ProcessHandle processHandle, MemoryEditor memoryEditor); + void applyIfNecessary(ProcessHandle processHandle); } @@ -22,10 +22,16 @@ public abstract class BaseCheat: Cheat { public abstract string name { get; } public abstract Combination keyboardShortcut { get; } - public abstract void applyIfNecessary(ProcessHandle processHandle, MemoryEditor memoryEditor); + protected abstract void apply(ProcessHandle processHandle); public SettableProperty isEnabled { get; } = new StoredProperty(); + public void applyIfNecessary(ProcessHandle processHandle) { + if (isEnabled.Value) { + apply(processHandle); + } + } + } } \ No newline at end of file diff --git a/TrainerCommon/Trainer/MemoryAddresses.cs b/TrainerCommon/Trainer/MemoryAddresses.cs index e97a54e..2b7d86f 100644 --- a/TrainerCommon/Trainer/MemoryAddresses.cs +++ b/TrainerCommon/Trainer/MemoryAddresses.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; #nullable enable @@ -25,38 +23,43 @@ public FixedMemoryAddress(IntPtr address) { public readonly struct IndirectMemoryAddress: MemoryAddress { - private readonly MemoryEditor memoryEditor; - - private readonly ProcessHandle processHandle; - private readonly string? moduleName; - private readonly IEnumerable offsets; + private readonly ProcessHandle processHandle; + private readonly string? moduleName; + private readonly int[] pointerOffsets; /// /// The name of the module to which the offsets are relative, such as UnityPlayer.dll, or null to use the process' main module. - /// - public IndirectMemoryAddress(ProcessHandle processHandle, string? moduleName, IEnumerable offsets) { - this.processHandle = processHandle; - this.moduleName = moduleName; - this.offsets = offsets; - memoryEditor = new MemoryEditorImpl(); + /// + public IndirectMemoryAddress(ProcessHandle processHandle, string? moduleName, int[] pointerOffsets) { + this.processHandle = processHandle; + this.moduleName = moduleName; + this.pointerOffsets = pointerOffsets; } public IntPtr address { get { - IntPtr moduleBaseAddress = memoryEditor.getModuleBaseAddressByName(processHandle, moduleName) ?? + /* + * Is the game 32-bit or 64-bit? + * Note that the trainer must be compiled as 64-bit in order to read memory from 64-bit games. + */ + int targetProcessWordLengthBytes = Win32.isProcess64Bit(processHandle.process) ? Marshal.SizeOf() : Marshal.SizeOf(); + + IntPtr memoryAddress = MemoryEditor.getModuleBaseAddressByName(processHandle, moduleName) ?? throw new ArgumentException($"No module with name {moduleName} found in process {processHandle.process.ProcessName}"); - int firstOffset = offsets.First(); - IntPtr memoryAddress = IntPtr.Add(moduleBaseAddress, firstOffset); - int targetProcessWordLengthBytes = Win32.isProcess64Bit(processHandle.process) ? Marshal.SizeOf() : Marshal.SizeOf(); + for (int offsetIndex = 0; offsetIndex < pointerOffsets.Length; offsetIndex++) { + int offset = pointerOffsets[offsetIndex]; - foreach (int offset in offsets.Skip(1)) { - bool success = Win32.ReadProcessMemory(processHandle.handle, memoryAddress, out IntPtr memoryValue, targetProcessWordLengthBytes, out long _); - if (!success) { - throw new ApplicationException($"Could not read memory address 0x{memoryAddress.ToInt64():X}: {Marshal.GetLastWin32Error()}"); - } + if (offsetIndex == 0) { + memoryAddress = IntPtr.Add(memoryAddress, offset); + } else { + bool success = Win32.ReadProcessMemory(processHandle.handle, memoryAddress, out IntPtr memoryValue, targetProcessWordLengthBytes, out long _); + if (!success) { + throw new ApplicationException($"Could not read memory address 0x{memoryAddress.ToInt64():X}: {Marshal.GetLastWin32Error()}"); + } - memoryAddress = IntPtr.Add(memoryValue, offset); + memoryAddress = IntPtr.Add(memoryValue, offset); + } } return memoryAddress; diff --git a/TrainerCommon/Trainer/MemoryEditor.cs b/TrainerCommon/Trainer/MemoryEditor.cs index ba174bb..d424dee 100644 --- a/TrainerCommon/Trainer/MemoryEditor.cs +++ b/TrainerCommon/Trainer/MemoryEditor.cs @@ -8,21 +8,9 @@ namespace TrainerCommon.Trainer { - public interface MemoryEditor { + public static class MemoryEditor { - ProcessHandle? openProcess(Process targetProcess); - - public T readFromProcessMemory(ProcessHandle processHandle, MemoryAddress memoryAddress, int? bytesToRead = null); - - void writeToProcessMemory(ProcessHandle processHandle, MemoryAddress memoryAddress, T value); - - IntPtr? getModuleBaseAddressByName(ProcessHandle processHandle, string? moduleName); - - } - - public class MemoryEditorImpl: MemoryEditor { - - public ProcessHandle? openProcess(Process targetProcess) { + public static ProcessHandle? openProcess(Process targetProcess) { IntPtr handle = Win32.OpenProcess( Win32.ProcessAccessFlags.VIRTUAL_MEMORY_READ | Win32.ProcessAccessFlags.VIRTUAL_MEMORY_WRITE | @@ -31,7 +19,7 @@ public class MemoryEditorImpl: MemoryEditor { return handle != IntPtr.Zero ? new ProcessHandle(targetProcess, handle) : null; } - public T readFromProcessMemory(ProcessHandle processHandle, MemoryAddress memoryAddress, int? bytesToRead = null) { + public static T readFromProcessMemory(ProcessHandle processHandle, MemoryAddress memoryAddress, int? bytesToRead = null) { bytesToRead ??= Marshal.SizeOf(); byte[] buffer = new byte[(int) bytesToRead]; @@ -45,7 +33,7 @@ public T readFromProcessMemory(ProcessHandle processHandle, MemoryAddress mem return convertBufferToType(buffer); } - public void writeToProcessMemory(ProcessHandle processHandle, MemoryAddress memoryAddress, T value) { + public static void writeToProcessMemory(ProcessHandle processHandle, MemoryAddress memoryAddress, T value) { byte[] buffer = convertValueToBuffer(value); bool writeSuccess = Win32.WriteProcessMemory(processHandle.handle, memoryAddress.address, buffer, buffer.Length, out IntPtr bytesWritten); @@ -65,7 +53,7 @@ private static byte[] convertValueToBuffer(T value) { }; } - public IntPtr? getModuleBaseAddressByName(ProcessHandle processHandle, string? moduleName) { + public static IntPtr? getModuleBaseAddressByName(ProcessHandle processHandle, string? moduleName) { return moduleName == null ? processHandle.process.MainModule?.BaseAddress : Win32.getModuleBaseAddress(new IntPtr(processHandle.process.Id), moduleName); } diff --git a/TrainerCommon/Trainer/TrainerService.cs b/TrainerCommon/Trainer/TrainerService.cs index 1446b5b..c924d66 100644 --- a/TrainerCommon/Trainer/TrainerService.cs +++ b/TrainerCommon/Trainer/TrainerService.cs @@ -14,7 +14,7 @@ namespace TrainerCommon.Trainer { public interface TrainerService: IDisposable { - Property isAttachedToGame { get; } + Property isAttachedToGame { get; } void attachToGame(Game game); @@ -23,14 +23,13 @@ public interface TrainerService: IDisposable { public class TrainerServiceImpl: TrainerService { private readonly StoredProperty attachmentState = new(); - public Property isAttachedToGame { get; } + public Property isAttachedToGame { get; } - private readonly MemoryEditor memoryEditor = new MemoryEditorImpl(); private readonly CancellationTokenSource cancellationTokenSource = new(); private Task? monitorTask; public TrainerServiceImpl() { - isAttachedToGame = DerivedProperty.Create(attachmentState, state => state == AttachmentState.ATTACHED); + isAttachedToGame = attachmentState; attachmentState.PropertyChanged += (_, args) => Trace.WriteLine($"Trainer state: {args.NewValue}"); } @@ -47,7 +46,7 @@ public void attachToGame(Game game) { while (!cancellationTokenSource.IsCancellationRequested) { await Task.Delay(attachmentState.Value switch { AttachmentState.TRAINER_STOPPED => 0, - AttachmentState.ATTACHED => 250, + AttachmentState.ATTACHED => 200, AttachmentState.MEMORY_ADDRESS_NOT_FOUND => 2000, AttachmentState.MEMORY_ADDRESS_COULD_NOT_BE_READ => 2000, AttachmentState.PROGRAM_NOT_RUNNING => 10000, @@ -64,7 +63,7 @@ public void attachToGame(Game game) { continue; } - gameProcessHandle ??= memoryEditor.openProcess(gameProcess); + gameProcessHandle ??= MemoryEditor.openProcess(gameProcess); if (gameProcessHandle == null) { attachmentState.Value = AttachmentState.PROGRAM_NOT_RUNNING; continue; @@ -72,7 +71,7 @@ public void attachToGame(Game game) { try { foreach (Cheat cheat in game.cheats) { - cheat.applyIfNecessary(gameProcessHandle, memoryEditor); + cheat.applyIfNecessary(gameProcessHandle); } attachmentState.Value = AttachmentState.ATTACHED; @@ -85,6 +84,9 @@ public void attachToGame(Game game) { if (e.NativeErrorCode != 299) { Console.WriteLine(e); } + + Trace.WriteLine("Memory address could not be read"); + Trace.WriteLine(e); } } @@ -96,19 +98,24 @@ public void attachToGame(Game game) { public void Dispose() { cancellationTokenSource.Cancel(); - monitorTask?.GetAwaiter().GetResult(); + try { + monitorTask?.GetAwaiter().GetResult(); + } catch (TaskCanceledException) { + //cancellation is how this task normally ends + } + attachmentState.Value = AttachmentState.TRAINER_STOPPED; } - private enum AttachmentState { + } - TRAINER_STOPPED, - PROGRAM_NOT_RUNNING, - MEMORY_ADDRESS_NOT_FOUND, - MEMORY_ADDRESS_COULD_NOT_BE_READ, - ATTACHED + public enum AttachmentState { - } + TRAINER_STOPPED, + PROGRAM_NOT_RUNNING, + MEMORY_ADDRESS_NOT_FOUND, + MEMORY_ADDRESS_COULD_NOT_BE_READ, + ATTACHED } diff --git a/TrainerCommon/TrainerCommon.csproj b/TrainerCommon/TrainerCommon.csproj index 50e7a2d..ec073a7 100644 --- a/TrainerCommon/TrainerCommon.csproj +++ b/TrainerCommon/TrainerCommon.csproj @@ -22,6 +22,7 @@ prompt 4 latest + IDE0055 pdbonly